arm: mvebu: Add gdsys ControlCenter-Compact board

The gdsys ControlCenter Digital board is based on a Marvell Armada 38x
SOC.

It boots from SPI-Flash but can be configured to boot from SD-card for
factory programming and testing.

On board peripherals include:
- 2 x GbE
- Xilinx Kintex-7 FPGA connected via PCIe
- mSATA
- USB3 host
- Atmel TPM

Signed-off-by: Dirk Eibach <dirk.eibach@gdsys.cc>
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Signed-off-by: Stefan Roese <sr@denx.de>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1eb373f..dacfe9a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1284,6 +1284,7 @@
 source "board/freescale/mx53loco/Kconfig"
 source "board/freescale/mx53smd/Kconfig"
 source "board/freescale/s32v234evb/Kconfig"
+source "board/gdsys/a38x/Kconfig"
 source "board/grinn/chiliboard/Kconfig"
 source "board/gumstix/pepper/Kconfig"
 source "board/h2200/Kconfig"
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 5f1df2a..0fbbb9b 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -82,7 +82,8 @@
 	armada-xp-gp.dtb			\
 	armada-xp-maxbcm.dtb			\
 	armada-xp-synology-ds414.dtb		\
-	armada-xp-theadorable.dtb
+	armada-xp-theadorable.dtb		\
+	armada-38x-controlcenterdc.dtb
 
 dtb-$(CONFIG_ARCH_UNIPHIER_LD11) += \
 	uniphier-ld11-ref.dtb
diff --git a/arch/arm/dts/armada-38x-controlcenterdc.dts b/arch/arm/dts/armada-38x-controlcenterdc.dts
new file mode 100644
index 0000000..d183fd7
--- /dev/null
+++ b/arch/arm/dts/armada-38x-controlcenterdc.dts
@@ -0,0 +1,589 @@
+/*
+ * Device Tree file for the Guntermann & Drunck ControlCenter-Compact board
+ *
+ * Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
+ *
+ * based on the Device Tree file for Marvell Armada 388 evaluation board
+ * (DB-88F6820), which is
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+/dts-v1/;
+
+#include "armada-388.dtsi"
+
+&gpio0 {
+	u-boot,dm-pre-reloc;
+};
+
+&gpio1 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart0 {
+	u-boot,dm-pre-reloc;
+};
+
+&uart1 {
+	u-boot,dm-pre-reloc;
+};
+
+/ {
+	model = "Controlcenter Digital Compact";
+	compatible = "marvell,a385-db", "marvell,armada388",
+		"marvell,armada385", "marvell,armada380";
+
+	chosen {
+		bootargs = "console=ttyS1,115200 earlyprintk";
+		stdout-path = "/soc/internal-regs/serial@12100";
+	};
+
+	aliases {
+		ethernet0 = &eth0;
+		ethernet2 = &eth2;
+		mdio-gpio0 = &MDIO0;
+		mdio-gpio1 = &MDIO1;
+		mdio-gpio2 = &MDIO2;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		i2c0 = &I2C0;
+		i2c1 = &I2C1;
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x10000000>; /* 256 MB */
+	};
+
+	clocks {
+		sc16isclk: sc16isclk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <11059200>;
+		};
+	};
+
+	soc {
+		ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+			  MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000>;
+
+		internal-regs {
+			spi0: spi@10600 {
+				status = "okay";
+				sc16is741: sc16is741@0 {
+					compatible = "nxp,sc16is741";
+					reg = <0>;
+					clocks = <&sc16isclk>;
+					spi-max-frequency = <4000000>;
+					interrupt-parent = <&gpio0>;
+					interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
+					gpio-controller;
+					#gpio-cells = <2>;
+				};
+			};
+
+			spi1: spi@10680 {
+				status = "okay";
+				u-boot,dm-pre-reloc;
+				spi-flash@0 {
+					#address-cells = <1>;
+					#size-cells = <1>;
+					compatible = "n25q016a";
+					reg = <0>; /* Chip select 0 */
+					spi-max-frequency = <108000000>;
+				};
+				spi-flash@1 {
+					#address-cells = <1>;
+					#size-cells = <1>;
+					compatible = "n25q128a11";
+					reg = <1>; /* Chip select 1 */
+					spi-max-frequency = <108000000>;
+					u-boot,dm-pre-reloc;
+				};
+			};
+
+			I2C0: i2c@11000 {
+				status = "okay";
+				clock-frequency = <1000000>;
+				u-boot,dm-pre-reloc;
+				PCA21: pca9698@21 {
+					compatible = "nxp,pca9698";
+					reg = <0x21>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+				PCA22: pca9698@22 {
+					compatible = "nxp,pca9698";
+					u-boot,dm-pre-reloc;
+					reg = <0x22>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+				PCA23: pca9698@23 {
+					compatible = "nxp,pca9698";
+					reg = <0x23>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+				PCA24: pca9698@24 {
+					compatible = "nxp,pca9698";
+					reg = <0x24>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+				PCA25: pca9698@25 {
+					compatible = "nxp,pca9698";
+					reg = <0x25>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+				PCA26: pca9698@26 {
+					compatible = "nxp,pca9698";
+					reg = <0x26>;
+					#gpio-cells = <2>;
+					gpio-controller;
+				};
+			};
+
+			I2C1: i2c@11100 {
+				status = "okay";
+				clock-frequency = <400000>;
+				at97sc3205t@29 {
+					compatible = "atmel,at97sc3204t";
+					reg = <0x29>;
+					u-boot,i2c-offset-len = <0>;
+				};
+				emc2305@2d {
+					compatible = "smsc,emc2305";
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0x2d>;
+					fan@0 {
+						reg = <0>;
+					};
+					fan@1 {
+						reg = <1>;
+					};
+					fan@2 {
+						reg = <2>;
+					};
+					fan@3 {
+						reg = <3>;
+					};
+					fan@4 {
+						reg = <4>;
+					};
+				};
+				lm77@48 {
+					compatible = "national,lm77";
+					reg = <0x48>;
+				};
+				ads1015@49 {
+					compatible = "ti,ads1015";
+					reg = <0x49>;
+				};
+				lm77@4a {
+					compatible = "national,lm77";
+					reg = <0x4a>;
+				};
+				ads1015@4b {
+					compatible = "ti,ads1015";
+					reg = <0x4b>;
+				};
+				emc2305@4c {
+					compatible = "smsc,emc2305";
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0x4c>;
+					fan@0 {
+						reg = <0>;
+					};
+					fan@1 {
+						reg = <1>;
+					};
+					fan@2 {
+						reg = <2>;
+					};
+					fan@3 {
+						reg = <3>;
+					};
+					fan@4 {
+						reg = <4>;
+					};
+				};
+				at24c512@54 {
+					compatible = "atmel,24c512";
+					reg = <0x54>;
+					u-boot,i2c-offset-len = <2>;
+				};
+				ds1339@68 {
+					compatible = "dallas,ds1339";
+					reg = <0x68>;
+				};
+			};
+
+			serial@12000 {
+				status = "okay";
+			};
+
+			serial@12100 {
+				status = "okay";
+			};
+
+			ethernet@34000 {
+				status = "okay";
+				phy = <&phy1>;
+				phy-mode = "sgmii";
+			};
+
+			usb@58000 {
+				status = "ok";
+			};
+
+			ethernet@70000 {
+				status = "okay";
+				phy = <&phy0>;
+				phy-mode = "sgmii";
+			};
+
+			mdio@72004 {
+				phy0: ethernet-phy@0 {
+					reg = <1>;
+				};
+
+				phy1: ethernet-phy@1 {
+					reg = <0>;
+				};
+			};
+
+			sata@a8000 {
+				status = "okay";
+			};
+
+			sdhci@d8000 {
+				broken-cd;
+				wp-inverted;
+				bus-width = <4>;
+				status = "okay";
+				no-1-8-v;
+			};
+
+			usb3@f0000 {
+				status = "okay";
+			};
+		};
+
+		pcie-controller {
+			status = "okay";
+			/*
+			 * The two PCIe units are accessible through
+			 * standard PCIe slots on the board.
+			 */
+			pcie@3,0 {
+				/* Port 0, Lane 0 */
+				status = "okay";
+			};
+		};
+
+		MDIO0: mdio0 {
+			compatible = "virtual,mdio-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			gpios = < /*MDC*/ &gpio0 13 0
+				  /*MDIO*/ &gpio0 14 0>;
+			mv88e1240@0 {
+				reg = <0x0>;
+			};
+			mv88e1240@1 {
+				reg = <0x1>;
+			};
+			mv88e1240@2 {
+				reg = <0x2>;
+			};
+			mv88e1240@3 {
+				reg = <0x3>;
+			};
+			mv88e1240@4 {
+				reg = <0x4>;
+			};
+			mv88e1240@5 {
+				reg = <0x5>;
+			};
+			mv88e1240@6 {
+				reg = <0x6>;
+			};
+			mv88e1240@7 {
+				reg = <0x7>;
+			};
+			mv88e1240@8 {
+				reg = <0x8>;
+			};
+			mv88e1240@9 {
+				reg = <0x9>;
+			};
+			mv88e1240@a {
+				reg = <0xa>;
+			};
+			mv88e1240@b {
+				reg = <0xb>;
+			};
+			mv88e1240@c {
+				reg = <0xc>;
+			};
+			mv88e1240@d {
+				reg = <0xd>;
+			};
+			mv88e1240@e {
+				reg = <0xe>;
+			};
+			mv88e1240@f {
+				reg = <0xf>;
+			};
+			mv88e1240@10 {
+				reg = <0x10>;
+			};
+			mv88e1240@11 {
+				reg = <0x11>;
+			};
+			mv88e1240@12 {
+				reg = <0x12>;
+			};
+			mv88e1240@13 {
+				reg = <0x13>;
+			};
+			mv88e1240@14 {
+				reg = <0x14>;
+			};
+			mv88e1240@15 {
+				reg = <0x15>;
+			};
+			mv88e1240@16 {
+				reg = <0x16>;
+			};
+			mv88e1240@17 {
+				reg = <0x17>;
+			};
+			mv88e1240@18 {
+				reg = <0x18>;
+			};
+			mv88e1240@19 {
+				reg = <0x19>;
+			};
+			mv88e1240@1a {
+				reg = <0x1a>;
+			};
+			mv88e1240@1b {
+				reg = <0x1b>;
+			};
+			mv88e1240@1c {
+				reg = <0x1c>;
+			};
+			mv88e1240@1d {
+				reg = <0x1d>;
+			};
+			mv88e1240@1e {
+				reg = <0x1e>;
+			};
+			mv88e1240@1f {
+				reg = <0x1f>;
+			};
+		};
+
+		MDIO1: mdio1 {
+			compatible = "virtual,mdio-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			gpios = < /*MDC*/ &gpio0 25 0
+				  /*MDIO*/ &gpio1 13 0>;
+			mv88e1240@0 {
+				reg = <0x0>;
+			};
+			mv88e1240@1 {
+				reg = <0x1>;
+			};
+			mv88e1240@2 {
+				reg = <0x2>;
+			};
+			mv88e1240@3 {
+				reg = <0x3>;
+			};
+			mv88e1240@4 {
+				reg = <0x4>;
+			};
+			mv88e1240@5 {
+				reg = <0x5>;
+			};
+			mv88e1240@6 {
+				reg = <0x6>;
+			};
+			mv88e1240@7 {
+				reg = <0x7>;
+			};
+			mv88e1240@8 {
+				reg = <0x8>;
+			};
+			mv88e1240@9 {
+				reg = <0x9>;
+			};
+			mv88e1240@a {
+				reg = <0xa>;
+			};
+			mv88e1240@b {
+				reg = <0xb>;
+			};
+			mv88e1240@c {
+				reg = <0xc>;
+			};
+			mv88e1240@d {
+				reg = <0xd>;
+			};
+			mv88e1240@e {
+				reg = <0xe>;
+			};
+			mv88e1240@f {
+				reg = <0xf>;
+			};
+			mv88e1240@10 {
+				reg = <0x10>;
+			};
+			mv88e1240@11 {
+				reg = <0x11>;
+			};
+			mv88e1240@12 {
+				reg = <0x12>;
+			};
+			mv88e1240@13 {
+				reg = <0x13>;
+			};
+			mv88e1240@14 {
+				reg = <0x14>;
+			};
+			mv88e1240@15 {
+				reg = <0x15>;
+			};
+			mv88e1240@16 {
+				reg = <0x16>;
+			};
+			mv88e1240@17 {
+				reg = <0x17>;
+			};
+			mv88e1240@18 {
+				reg = <0x18>;
+			};
+			mv88e1240@19 {
+				reg = <0x19>;
+			};
+			mv88e1240@1a {
+				reg = <0x1a>;
+			};
+			mv88e1240@1b {
+				reg = <0x1b>;
+			};
+			mv88e1240@1c {
+				reg = <0x1c>;
+			};
+			mv88e1240@1d {
+				reg = <0x1d>;
+			};
+			mv88e1240@1e {
+				reg = <0x1e>;
+			};
+			mv88e1240@1f {
+				reg = <0x1f>;
+			};
+		};
+
+		MDIO2: mdio2 {
+			compatible = "virtual,mdio-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			gpios = < /*MDC*/ &gpio1 14 0
+				  /*MDIO*/ &gpio0 24 0>;
+			mv88e1240@0 {
+				reg = <0x0>;
+			};
+			mv88e1240@1 {
+				reg = <0x1>;
+			};
+			mv88e1240@2 {
+				reg = <0x2>;
+			};
+			mv88e1240@3 {
+				reg = <0x3>;
+			};
+			mv88e1240@4 {
+				reg = <0x4>;
+			};
+			mv88e1240@5 {
+				reg = <0x5>;
+			};
+			mv88e1240@6 {
+				reg = <0x6>;
+			};
+			mv88e1240@7 {
+				reg = <0x7>;
+			};
+			mv88e1240@8 {
+				reg = <0x8>;
+			};
+			mv88e1240@9 {
+				reg = <0x9>;
+			};
+			mv88e1240@a {
+				reg = <0xa>;
+			};
+			mv88e1240@b {
+				reg = <0xb>;
+			};
+			mv88e1240@c {
+				reg = <0xc>;
+			};
+			mv88e1240@d {
+				reg = <0xd>;
+			};
+			mv88e1240@e {
+				reg = <0xe>;
+			};
+			mv88e1240@f {
+				reg = <0xf>;
+			};
+			mv88e1240@10 {
+				reg = <0x10>;
+			};
+			mv88e1240@11 {
+				reg = <0x11>;
+			};
+			mv88e1240@12 {
+				reg = <0x12>;
+			};
+			mv88e1240@13 {
+				reg = <0x13>;
+			};
+			mv88e1240@14 {
+				reg = <0x14>;
+			};
+			mv88e1240@15 {
+				reg = <0x15>;
+			};
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		finder_led {
+			label = "finder-led";
+			gpios = <&PCA22 25 0>;
+		};
+
+		status_led {
+			label = "status-led";
+			gpios = <&gpio0 29 0>;
+		};
+	};
+};
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index a256c2f..6ae54ef 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -112,6 +112,10 @@
 	select BOARD_LATE_INIT if USB
 	select MV78260
 
+config TARGET_CONTROLCENTERDC
+	bool "Support CONTROLCENTERDC"
+	select 88F6820
+
 endchoice
 
 config SYS_BOARD
diff --git a/board/gdsys/a38x/.gitignore b/board/gdsys/a38x/.gitignore
new file mode 100644
index 0000000..775b934
--- /dev/null
+++ b/board/gdsys/a38x/.gitignore
@@ -0,0 +1 @@
+kwbimage.cfg
diff --git a/board/gdsys/a38x/Kconfig b/board/gdsys/a38x/Kconfig
new file mode 100644
index 0000000..3fdef64
--- /dev/null
+++ b/board/gdsys/a38x/Kconfig
@@ -0,0 +1,36 @@
+if TARGET_CONTROLCENTERDC
+
+config SYS_BOARD
+	default "a38x"
+
+config SYS_VENDOR
+	default "gdsys"
+
+config SYS_SOC
+	default "mvebu"
+
+config SYS_CONFIG_NAME
+	default "controlcenterdc"
+
+menu "Controlcenter DC board options"
+
+choice
+	prompt "Select boot method"
+
+config SPL_BOOT_DEVICE_SPI
+	bool "SPI"
+
+config SPL_BOOT_DEVICE_MMC
+	bool "MMC"
+	select SPL_LIBDISK_SUPPORT
+
+endchoice
+
+#config SPL_BOOT_DEVICE
+#	int
+#	default 1 if SPL_BOOT_DEVICE_SPI
+#	default 2 if SPL_BOOT_DEVICE_MMC
+
+endmenu
+
+endif
diff --git a/board/gdsys/a38x/MAINTAINERS b/board/gdsys/a38x/MAINTAINERS
new file mode 100644
index 0000000..3cb9b63
--- /dev/null
+++ b/board/gdsys/a38x/MAINTAINERS
@@ -0,0 +1,7 @@
+A38X BOARD
+M:	Dirk Eibach <eibach@gdsys.cc>
+M:	Mario Six <six@gdsys.de>
+S:	Maintained
+F:	board/gdsys/a38x/
+F:	include/configs/controlcenterdc.h
+F:	configs/controlcenterdc_defconfig
diff --git a/board/gdsys/a38x/Makefile b/board/gdsys/a38x/Makefile
new file mode 100644
index 0000000..e1f0bd8
--- /dev/null
+++ b/board/gdsys/a38x/Makefile
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2015 Stefan Roese <sr@denx.de>
+# Copyright (C) 2015 Reinhard Pfau <reinhard.pfau@gdsys.cc>
+# Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_TARGET_CONTROLCENTERDC) += controlcenterdc.o hre.o spl.o keyprogram.o dt_helpers.o
+
+ifeq ($(CONFIG_SPL_BUILD),)
+
+obj-$(CONFIG_TARGET_CONTROLCENTERDC) += hydra.o ihs_phys.o
+
+extra-$(CONFIG_TARGET_CONTROLCENTERDC) += kwbimage.cfg
+
+KWB_REPLACE += BOOT_FROM
+ifneq ($(CONFIG_SPL_BOOT_DEVICE_SPI),)
+	KWB_CFG_BOOT_FROM=spi
+endif
+ifneq ($(CONFIG_SPL_BOOT_DEVICE_MMC),)
+	KWB_CFG_BOOT_FROM=sdio
+endif
+
+ifneq ($(CONFIG_SECURED_MODE_IMAGE),)
+KWB_REPLACE += CSK_INDEX
+KWB_CFG_CSK_INDEX = $(CONFIG_SECURED_MODE_CSK_INDEX)
+
+KWB_REPLACE += SEC_BOOT_DEV
+KWB_CFG_SEC_BOOT_DEV=$(patsubst "%",%, \
+	$(if $(findstring BOOT_SPI_NOR_FLASH,$(CONFIG_SPL_BOOT_DEVICE)),0x34) \
+	$(if $(findstring BOOT_SDIO_MMC_CARD,$(CONFIG_SPL_BOOT_DEVICE)),0x31) \
+	)
+
+KWB_REPLACE += SEC_FUSE_DUMP
+KWB_CFG_SEC_FUSE_DUMP = a38x
+endif
+
+$(src)/kwbimage.cfg: $(src)/kwbimage.cfg.in include/autoconf.mk \
+		include/config/auto.conf
+	$(Q)sed -ne '$(foreach V,$(KWB_REPLACE),s/^#@$(V)/$(V) $(KWB_CFG_$(V))/;)p' \
+	<$< >$(dir $<)$(@F)
+
+endif
diff --git a/board/gdsys/a38x/controlcenterdc.c b/board/gdsys/a38x/controlcenterdc.c
new file mode 100644
index 0000000..f0efb53
--- /dev/null
+++ b/board/gdsys/a38x/controlcenterdc.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2015 Stefan Roese <sr@denx.de>
+ * Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <miiphy.h>
+#include <tpm.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm-generic/gpio.h>
+
+#include "../drivers/ddr/marvell/a38x/ddr3_a38x_topology.h"
+#include "../arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h"
+
+#include "keyprogram.h"
+#include "dt_helpers.h"
+#include "hydra.h"
+#include "ihs_phys.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ETH_PHY_CTRL_REG		0
+#define ETH_PHY_CTRL_POWER_DOWN_BIT	11
+#define ETH_PHY_CTRL_POWER_DOWN_MASK	(1 << ETH_PHY_CTRL_POWER_DOWN_BIT)
+
+#define DB_GP_88F68XX_GPP_OUT_ENA_LOW	0x7fffffff
+#define DB_GP_88F68XX_GPP_OUT_ENA_MID	0xffffefff
+
+#define DB_GP_88F68XX_GPP_OUT_VAL_LOW	0x0
+#define DB_GP_88F68XX_GPP_OUT_VAL_MID	0x00001000
+#define DB_GP_88F68XX_GPP_POL_LOW	0x0
+#define DB_GP_88F68XX_GPP_POL_MID	0x0
+
+/*
+ * Define the DDR layout / topology here in the board file. This will
+ * be used by the DDR3 init code in the SPL U-Boot version to configure
+ * the DDR3 controller.
+ */
+static struct hws_topology_map ddr_topology_map = {
+	0x1, /* active interfaces */
+	/* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
+	{ { { {0x1, 0, 0, 0},
+	      {0x1, 0, 0, 0},
+	      {0x1, 0, 0, 0},
+	      {0x1, 0, 0, 0},
+	      {0x1, 0, 0, 0} },
+	    SPEED_BIN_DDR_1600K,	/* speed_bin */
+	    BUS_WIDTH_16,		/* memory_width */
+	    MEM_4G,			/* mem_size */
+	    DDR_FREQ_533,		/* frequency */
+	    0, 0,			/* cas_l cas_wl */
+	    HWS_TEMP_LOW} },		/* temperature */
+	5,				/* Num Of Bus Per Interface*/
+	BUS_MASK_32BIT			/* Busses mask */
+};
+
+static struct serdes_map serdes_topology_map[] = {
+	{SGMII0, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
+	{USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
+	/* SATA tx polarity is inverted */
+	{SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 1},
+	{SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
+	{DEFAULT_SERDES, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0},
+	{PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}
+};
+
+int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
+{
+	*serdes_map_array = serdes_topology_map;
+	*count = ARRAY_SIZE(serdes_topology_map);
+	return 0;
+}
+
+void board_pex_config(void)
+{
+#ifdef CONFIG_SPL_BUILD
+	uint k;
+	struct gpio_desc gpio = {};
+
+	if (!request_gpio_by_name(&gpio, "pca9698@22", 31, "fpga-program-gpio")) {
+		/* prepare FPGA reconfiguration */
+		dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT);
+		dm_gpio_set_value(&gpio, 0);
+
+		/* give lunatic PCIe clock some time to stabilize */
+		mdelay(500);
+
+		/* start FPGA reconfiguration */
+		dm_gpio_set_dir_flags(&gpio, GPIOD_IS_IN);
+	}
+
+	/* wait for FPGA done */
+	if (!request_gpio_by_name(&gpio, "pca9698@22", 19, "fpga-done-gpio")) {
+		for (k = 0; k < 20; ++k) {
+			if (dm_gpio_get_value(&gpio)) {
+				printf("FPGA done after %u rounds\n", k);
+				break;
+			}
+			mdelay(100);
+		}
+	}
+
+	/* disable FPGA reset */
+	if (!request_gpio_by_name(&gpio, "gpio@18100", 6, "cpu-to-fpga-reset")) {
+		dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT);
+		dm_gpio_set_value(&gpio, 1);
+	}
+
+	/* wait for FPGA ready */
+	if (!request_gpio_by_name(&gpio, "pca9698@22", 27, "fpga-ready-gpio")) {
+		for (k = 0; k < 2; ++k) {
+			if (!dm_gpio_get_value(&gpio))
+				break;
+			mdelay(100);
+		}
+	}
+#endif
+}
+
+struct hws_topology_map *ddr3_get_topology_map(void)
+{
+	return &ddr_topology_map;
+}
+
+int board_early_init_f(void)
+{
+#ifdef CONFIG_SPL_BUILD
+	/* Configure MPP */
+	writel(0x00111111, MVEBU_MPP_BASE + 0x00);
+	writel(0x40040000, MVEBU_MPP_BASE + 0x04);
+	writel(0x00466444, MVEBU_MPP_BASE + 0x08);
+	writel(0x00043300, MVEBU_MPP_BASE + 0x0c);
+	writel(0x44400000, MVEBU_MPP_BASE + 0x10);
+	writel(0x20000334, MVEBU_MPP_BASE + 0x14);
+	writel(0x40000000, MVEBU_MPP_BASE + 0x18);
+	writel(0x00004444, MVEBU_MPP_BASE + 0x1c);
+
+	/* Set GPP Out value */
+	writel(DB_GP_88F68XX_GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
+	writel(DB_GP_88F68XX_GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
+
+	/* Set GPP Polarity */
+	writel(DB_GP_88F68XX_GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
+	writel(DB_GP_88F68XX_GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
+
+	/* Set GPP Out Enable */
+	writel(DB_GP_88F68XX_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
+	writel(DB_GP_88F68XX_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
+#endif
+
+	return 0;
+}
+
+int board_init(void)
+{
+	/* Address of boot parameters */
+	gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
+
+	return 0;
+}
+
+#ifndef CONFIG_SPL_BUILD
+void init_host_phys(struct mii_dev *bus)
+{
+	uint k;
+
+	for (k = 0; k < 2; ++k) {
+		struct phy_device *phydev;
+
+		phydev = phy_find_by_mask(bus, 1 << k,
+					  PHY_INTERFACE_MODE_SGMII);
+
+		if (phydev)
+			phy_config(phydev);
+	}
+}
+
+int ccdc_eth_init(void)
+{
+	uint k;
+	uint octo_phy_mask = 0;
+	int ret;
+	struct mii_dev *bus;
+
+	/* Init SoC's phys */
+	bus = miiphy_get_dev_by_name("ethernet@34000");
+
+	if (bus)
+		init_host_phys(bus);
+
+	bus = miiphy_get_dev_by_name("ethernet@70000");
+
+	if (bus)
+		init_host_phys(bus);
+
+	/* Init octo phys */
+	octo_phy_mask = calculate_octo_phy_mask();
+
+	printf("IHS PHYS: %08x", octo_phy_mask);
+
+	ret = init_octo_phys(octo_phy_mask);
+
+	if (ret)
+		return ret;
+
+	printf("\n");
+
+	if (!get_fpga()) {
+		puts("fpga was NULL\n");
+		return 1;
+	}
+
+	/* reset all FPGA-QSGMII instances */
+	for (k = 0; k < 80; ++k)
+		writel(1 << 31, get_fpga()->qsgmii_port_state[k]);
+
+	udelay(100);
+
+	for (k = 0; k < 80; ++k)
+		writel(0, get_fpga()->qsgmii_port_state[k]);
+	return 0;
+}
+
+#endif
+
+int board_late_init(void)
+{
+#ifndef CONFIG_SPL_BUILD
+	hydra_initialize();
+#endif
+	return 0;
+}
+
+int board_fix_fdt(void *rw_fdt_blob)
+{
+	struct udevice *bus = NULL;
+	uint k;
+	char name[64];
+	int err;
+
+	err = uclass_get_device_by_name(UCLASS_I2C, "i2c@11000", &bus);
+
+	if (err) {
+		printf("Could not get I2C bus.\n");
+		return err;
+	}
+
+	for (k = 0x21; k <= 0x26; k++) {
+		snprintf(name, 64,
+			 "/soc/internal-regs/i2c@11000/pca9698@%02x", k);
+
+		if (!dm_i2c_simple_probe(bus, k))
+			fdt_disable_by_ofname(rw_fdt_blob, name);
+	}
+
+	return 0;
+}
+
+int last_stage_init(void)
+{
+#ifndef CONFIG_SPL_BUILD
+	ccdc_eth_init();
+#endif
+	if (tpm_init() || tpm_startup(TPM_ST_CLEAR) ||
+	    tpm_continue_self_test()) {
+		return 1;
+	}
+
+	mdelay(37);
+
+	flush_keys();
+	load_and_run_keyprog();
+
+	return 0;
+}
diff --git a/board/gdsys/a38x/dt_helpers.c b/board/gdsys/a38x/dt_helpers.c
new file mode 100644
index 0000000..759d82a
--- /dev/null
+++ b/board/gdsys/a38x/dt_helpers.c
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <fdt_support.h>
+#include <asm-generic/gpio.h>
+#include <dm.h>
+
+int fdt_disable_by_ofname(void *rw_fdt_blob, char *ofname)
+{
+	int offset = fdt_path_offset(rw_fdt_blob, ofname);
+
+	return fdt_status_disabled(rw_fdt_blob, offset);
+}
+
+bool dm_i2c_simple_probe(struct udevice *bus, uint chip_addr)
+{
+	struct udevice *dev;
+
+	return !dm_i2c_probe(bus, chip_addr, DM_I2C_CHIP_RD_ADDRESS |
+			     DM_I2C_CHIP_WR_ADDRESS, &dev);
+}
+
+int request_gpio_by_name(struct gpio_desc *gpio, const char *gpio_dev_name,
+			 uint offset, char *gpio_name)
+{
+	struct udevice *gpio_dev = NULL;
+
+	if (uclass_get_device_by_name(UCLASS_GPIO, gpio_dev_name, &gpio_dev))
+		return 1;
+
+	gpio->dev = gpio_dev;
+	gpio->offset = offset;
+	gpio->flags = 0;
+
+	return dm_gpio_request(gpio, gpio_name);
+}
+
diff --git a/board/gdsys/a38x/dt_helpers.h b/board/gdsys/a38x/dt_helpers.h
new file mode 100644
index 0000000..1b95262
--- /dev/null
+++ b/board/gdsys/a38x/dt_helpers.h
@@ -0,0 +1,16 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __DT_HELPERS_H
+#define __DT_HELPERS_H
+
+int fdt_disable_by_ofname(void *rw_fdt_blob, char *ofname);
+bool dm_i2c_simple_probe(struct udevice *bus, uint chip_addr);
+int request_gpio_by_name(struct gpio_desc *gpio, const char *gpio_dev_name,
+			 uint offset, char *gpio_name);
+
+#endif /* __DT_HELPERS_H */
diff --git a/board/gdsys/a38x/hre.c b/board/gdsys/a38x/hre.c
new file mode 100644
index 0000000..1689d44
--- /dev/null
+++ b/board/gdsys/a38x/hre.c
@@ -0,0 +1,516 @@
+/*
+ * (C) Copyright 2013
+ * Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <fs.h>
+#include <i2c.h>
+#include <mmc.h>
+#include <tpm.h>
+#include <u-boot/sha1.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <pca9698.h>
+
+#include "hre.h"
+
+/* other constants */
+enum {
+	ESDHC_BOOT_IMAGE_SIG_OFS	= 0x40,
+	ESDHC_BOOT_IMAGE_SIZE_OFS	= 0x48,
+	ESDHC_BOOT_IMAGE_ADDR_OFS	= 0x50,
+	ESDHC_BOOT_IMAGE_TARGET_OFS	= 0x58,
+	ESDHC_BOOT_IMAGE_ENTRY_OFS	= 0x60,
+};
+
+enum {
+	I2C_SOC_0 = 0,
+	I2C_SOC_1 = 1,
+};
+
+enum access_mode {
+	HREG_NONE	= 0,
+	HREG_RD		= 1,
+	HREG_WR		= 2,
+	HREG_RDWR	= 3,
+};
+
+/* register constants */
+enum {
+	FIX_HREG_DEVICE_ID_HASH	= 0,
+	FIX_HREG_UNUSED1	= 1,
+	FIX_HREG_UNUSED2	= 2,
+	FIX_HREG_VENDOR		= 3,
+	COUNT_FIX_HREGS
+};
+
+static struct h_reg pcr_hregs[24];
+static struct h_reg fix_hregs[COUNT_FIX_HREGS];
+static struct h_reg var_hregs[8];
+
+/* hre opcodes */
+enum {
+	/* opcodes w/o data */
+	HRE_NOP		= 0x00,
+	HRE_SYNC	= HRE_NOP,
+	HRE_CHECK0	= 0x01,
+	/* opcodes w/o data, w/ sync dst */
+	/* opcodes w/ data */
+	HRE_LOAD	= 0x81,
+	/* opcodes w/data, w/sync dst */
+	HRE_XOR		= 0xC1,
+	HRE_AND		= 0xC2,
+	HRE_OR		= 0xC3,
+	HRE_EXTEND	= 0xC4,
+	HRE_LOADKEY	= 0xC5,
+};
+
+/* hre errors */
+enum {
+	HRE_E_OK	= 0,
+	HRE_E_TPM_FAILURE,
+	HRE_E_INVALID_HREG,
+};
+
+static uint64_t device_id;
+static uint64_t device_cl;
+static uint64_t device_type;
+
+static uint32_t platform_key_handle;
+
+static uint32_t hre_tpm_err;
+static int hre_err = HRE_E_OK;
+
+#define IS_PCR_HREG(spec) ((spec) & 0x20)
+#define IS_FIX_HREG(spec) (((spec) & 0x38) == 0x08)
+#define IS_VAR_HREG(spec) (((spec) & 0x38) == 0x10)
+#define HREG_IDX(spec) ((spec) & (IS_PCR_HREG(spec) ? 0x1f : 0x7))
+
+static const uint8_t vendor[] = "Guntermann & Drunck";
+
+/**
+ * @brief get the size of a given (TPM) NV area
+ * @param index	NV index of the area to get size for
+ * @param size	pointer to the size
+ * @return 0 on success, != 0 on error
+ */
+static int get_tpm_nv_size(uint32_t index, uint32_t *size)
+{
+	uint32_t err;
+	uint8_t info[72];
+	uint8_t *ptr;
+	uint16_t v16;
+
+	err = tpm_get_capability(TPM_CAP_NV_INDEX, index,
+		info, sizeof(info));
+	if (err) {
+		printf("tpm_get_capability(CAP_NV_INDEX, %08x) failed: %u\n",
+		       index, err);
+		return 1;
+	}
+
+	/* skip tag and nvIndex */
+	ptr = info + 6;
+	/* skip 2 pcr info fields */
+	v16 = get_unaligned_be16(ptr);
+	ptr += 2 + v16 + 1 + 20;
+	v16 = get_unaligned_be16(ptr);
+	ptr += 2 + v16 + 1 + 20;
+	/* skip permission and flags */
+	ptr += 6 + 3;
+
+	*size = get_unaligned_be32(ptr);
+	return 0;
+}
+
+/**
+ * @brief search for a key by usage auth and pub key hash.
+ * @param auth	usage auth of the key to search for
+ * @param pubkey_digest	(SHA1) hash of the pub key structure of the key
+ * @param[out] handle	the handle of the key iff found
+ * @return 0 if key was found in TPM; != 0 if not.
+ */
+static int find_key(const uint8_t auth[20], const uint8_t pubkey_digest[20],
+		uint32_t *handle)
+{
+	uint16_t key_count;
+	uint32_t key_handles[10];
+	uint8_t buf[288];
+	uint8_t *ptr;
+	uint32_t err;
+	uint8_t digest[20];
+	size_t buf_len;
+	unsigned int i;
+
+	/* fetch list of already loaded keys in the TPM */
+	err = tpm_get_capability(TPM_CAP_HANDLE, TPM_RT_KEY, buf, sizeof(buf));
+	if (err)
+		return -1;
+	key_count = get_unaligned_be16(buf);
+	ptr = buf + 2;
+	for (i = 0; i < key_count; ++i, ptr += 4)
+		key_handles[i] = get_unaligned_be32(ptr);
+
+	/* now search a(/ the) key which we can access with the given auth */
+	for (i = 0; i < key_count; ++i) {
+		buf_len = sizeof(buf);
+		err = tpm_get_pub_key_oiap(key_handles[i], auth, buf, &buf_len);
+		if (err && err != TPM_AUTHFAIL)
+			return -1;
+		if (err)
+			continue;
+		sha1_csum(buf, buf_len, digest);
+		if (!memcmp(digest, pubkey_digest, 20)) {
+			*handle = key_handles[i];
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * @brief read CCDM common data from TPM NV
+ * @return 0 if CCDM common data was found and read, !=0 if something failed.
+ */
+static int read_common_data(void)
+{
+	uint32_t size = 0;
+	uint32_t err;
+	uint8_t buf[256];
+	sha1_context ctx;
+
+	if (get_tpm_nv_size(NV_COMMON_DATA_INDEX, &size) ||
+	    size < NV_COMMON_DATA_MIN_SIZE)
+		return 1;
+	err = tpm_nv_read_value(NV_COMMON_DATA_INDEX,
+		buf, min(sizeof(buf), size));
+	if (err) {
+		printf("tpm_nv_read_value() failed: %u\n", err);
+		return 1;
+	}
+
+	device_id = get_unaligned_be64(buf);
+	device_cl = get_unaligned_be64(buf + 8);
+	device_type = get_unaligned_be64(buf + 16);
+
+	sha1_starts(&ctx);
+	sha1_update(&ctx, buf, 24);
+	sha1_finish(&ctx, fix_hregs[FIX_HREG_DEVICE_ID_HASH].digest);
+	fix_hregs[FIX_HREG_DEVICE_ID_HASH].valid = true;
+
+	platform_key_handle = get_unaligned_be32(buf + 24);
+
+	return 0;
+}
+
+/**
+ * @brief get pointer to  hash register by specification
+ * @param spec	specification of a hash register
+ * @return pointer to hash register or NULL if @a spec does not qualify a
+ * valid hash register; NULL else.
+ */
+static struct h_reg *get_hreg(uint8_t spec)
+{
+	uint8_t idx;
+
+	idx = HREG_IDX(spec);
+	if (IS_FIX_HREG(spec)) {
+		if (idx < ARRAY_SIZE(fix_hregs))
+			return fix_hregs + idx;
+		hre_err = HRE_E_INVALID_HREG;
+	} else if (IS_PCR_HREG(spec)) {
+		if (idx < ARRAY_SIZE(pcr_hregs))
+			return pcr_hregs + idx;
+		hre_err = HRE_E_INVALID_HREG;
+	} else if (IS_VAR_HREG(spec)) {
+		if (idx < ARRAY_SIZE(var_hregs))
+			return var_hregs + idx;
+		hre_err = HRE_E_INVALID_HREG;
+	}
+	return NULL;
+}
+
+/**
+ * @brief get pointer of a hash register by specification and usage.
+ * @param spec	specification of a hash register
+ * @param mode	access mode (read or write or read/write)
+ * @return pointer to hash register if found and valid; NULL else.
+ *
+ * This func uses @a get_reg() to determine the hash register for a given spec.
+ * If a register is found it is validated according to the desired access mode.
+ * The value of automatic registers (PCR register and fixed registers) is
+ * loaded or computed on read access.
+ */
+static struct h_reg *access_hreg(uint8_t spec, enum access_mode mode)
+{
+	struct h_reg *result;
+
+	result = get_hreg(spec);
+	if (!result)
+		return NULL;
+
+	if (mode & HREG_WR) {
+		if (IS_FIX_HREG(spec)) {
+			hre_err = HRE_E_INVALID_HREG;
+			return NULL;
+		}
+	}
+	if (mode & HREG_RD) {
+		if (!result->valid) {
+			if (IS_PCR_HREG(spec)) {
+				hre_tpm_err = tpm_pcr_read(HREG_IDX(spec),
+					result->digest, 20);
+				result->valid = (hre_tpm_err == TPM_SUCCESS);
+			} else if (IS_FIX_HREG(spec)) {
+				switch (HREG_IDX(spec)) {
+				case FIX_HREG_DEVICE_ID_HASH:
+					read_common_data();
+					break;
+				case FIX_HREG_VENDOR:
+					memcpy(result->digest, vendor, 20);
+					result->valid = true;
+					break;
+				}
+			} else {
+				result->valid = true;
+			}
+		}
+		if (!result->valid) {
+			hre_err = HRE_E_INVALID_HREG;
+			return NULL;
+		}
+	}
+
+	return result;
+}
+
+static void *compute_and(void *_dst, const void *_src, size_t n)
+{
+	uint8_t *dst = _dst;
+	const uint8_t *src = _src;
+	size_t i;
+
+	for (i = n; i-- > 0; )
+		*dst++ &= *src++;
+
+	return _dst;
+}
+
+static void *compute_or(void *_dst, const void *_src, size_t n)
+{
+	uint8_t *dst = _dst;
+	const uint8_t *src = _src;
+	size_t i;
+
+	for (i = n; i-- > 0; )
+		*dst++ |= *src++;
+
+	return _dst;
+}
+
+static void *compute_xor(void *_dst, const void *_src, size_t n)
+{
+	uint8_t *dst = _dst;
+	const uint8_t *src = _src;
+	size_t i;
+
+	for (i = n; i-- > 0; )
+		*dst++ ^= *src++;
+
+	return _dst;
+}
+
+static void *compute_extend(void *_dst, const void *_src, size_t n)
+{
+	uint8_t digest[20];
+	sha1_context ctx;
+
+	sha1_starts(&ctx);
+	sha1_update(&ctx, _dst, n);
+	sha1_update(&ctx, _src, n);
+	sha1_finish(&ctx, digest);
+	memcpy(_dst, digest, min(n, sizeof(digest)));
+
+	return _dst;
+}
+
+static int hre_op_loadkey(struct h_reg *src_reg, struct h_reg *dst_reg,
+		const void *key, size_t key_size)
+{
+	uint32_t parent_handle;
+	uint32_t key_handle;
+
+	if (!src_reg || !dst_reg || !src_reg->valid || !dst_reg->valid)
+		return -1;
+	if (find_key(src_reg->digest, dst_reg->digest, &parent_handle))
+		return -1;
+	hre_tpm_err = tpm_load_key2_oiap(parent_handle, key, key_size,
+		src_reg->digest, &key_handle);
+	if (hre_tpm_err) {
+		hre_err = HRE_E_TPM_FAILURE;
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief executes the next opcode on the hash register engine.
+ * @param[in,out] ip	pointer to the opcode (instruction pointer)
+ * @param[in,out] code_size	(remaining) size of the code
+ * @return new instruction pointer on success, NULL on error.
+ */
+static const uint8_t *hre_execute_op(const uint8_t **ip, size_t *code_size)
+{
+	bool dst_modified = false;
+	uint32_t ins;
+	uint8_t opcode;
+	uint8_t src_spec;
+	uint8_t dst_spec;
+	uint16_t data_size;
+	struct h_reg *src_reg, *dst_reg;
+	uint8_t buf[20];
+	const uint8_t *src_buf, *data;
+	uint8_t *ptr;
+	int i;
+	void * (*bin_func)(void *, const void *, size_t);
+
+	if (*code_size < 4)
+		return NULL;
+
+	ins = get_unaligned_be32(*ip);
+	opcode = **ip;
+	data = *ip + 4;
+	src_spec = (ins >> 18) & 0x3f;
+	dst_spec = (ins >> 12) & 0x3f;
+	data_size = (ins & 0x7ff);
+
+	debug("HRE: ins=%08x (op=%02x, s=%02x, d=%02x, L=%d)\n", ins,
+	      opcode, src_spec, dst_spec, data_size);
+
+	if ((opcode & 0x80) && (data_size + 4) > *code_size)
+		return NULL;
+
+	src_reg = access_hreg(src_spec, HREG_RD);
+	if (hre_err || hre_tpm_err)
+		return NULL;
+	dst_reg = access_hreg(dst_spec, (opcode & 0x40) ? HREG_RDWR : HREG_WR);
+	if (hre_err || hre_tpm_err)
+		return NULL;
+
+	switch (opcode) {
+	case HRE_NOP:
+		goto end;
+	case HRE_CHECK0:
+		if (src_reg) {
+			for (i = 0; i < 20; ++i) {
+				if (src_reg->digest[i])
+					return NULL;
+			}
+		}
+		break;
+	case HRE_LOAD:
+		bin_func = memcpy;
+		goto do_bin_func;
+	case HRE_XOR:
+		bin_func = compute_xor;
+		goto do_bin_func;
+	case HRE_AND:
+		bin_func = compute_and;
+		goto do_bin_func;
+	case HRE_OR:
+		bin_func = compute_or;
+		goto do_bin_func;
+	case HRE_EXTEND:
+		bin_func = compute_extend;
+do_bin_func:
+		if (!dst_reg)
+			return NULL;
+		if (src_reg) {
+			src_buf = src_reg->digest;
+		} else {
+			if (!data_size) {
+				memset(buf, 0, 20);
+				src_buf = buf;
+			} else if (data_size == 1) {
+				memset(buf, *data, 20);
+				src_buf = buf;
+			} else if (data_size >= 20) {
+				src_buf = data;
+			} else {
+				src_buf = buf;
+				for (ptr = (uint8_t *)src_buf, i = 20; i > 0;
+					i -= data_size, ptr += data_size)
+					memcpy(ptr, data,
+					       min_t(size_t, i, data_size));
+			}
+		}
+		bin_func(dst_reg->digest, src_buf, 20);
+		dst_reg->valid = true;
+		dst_modified = true;
+		break;
+	case HRE_LOADKEY:
+		if (hre_op_loadkey(src_reg, dst_reg, data, data_size))
+			return NULL;
+		break;
+	default:
+		return NULL;
+	}
+
+	if (dst_reg && dst_modified && IS_PCR_HREG(dst_spec)) {
+		hre_tpm_err = tpm_extend(HREG_IDX(dst_spec), dst_reg->digest,
+			dst_reg->digest);
+		if (hre_tpm_err) {
+			hre_err = HRE_E_TPM_FAILURE;
+			return NULL;
+		}
+	}
+end:
+	*ip += 4;
+	*code_size -= 4;
+	if (opcode & 0x80) {
+		*ip += data_size;
+		*code_size -= data_size;
+	}
+
+	return *ip;
+}
+
+/**
+ * @brief runs a program on the hash register engine.
+ * @param code		pointer to the (HRE) code.
+ * @param code_size	size of the code (in bytes).
+ * @return 0 on success, != 0 on failure.
+ */
+int hre_run_program(const uint8_t *code, size_t code_size)
+{
+	size_t code_left;
+	const uint8_t *ip = code;
+
+	code_left = code_size;
+	hre_tpm_err = 0;
+	hre_err = HRE_E_OK;
+	while (code_left > 0)
+		if (!hre_execute_op(&ip, &code_left))
+			return -1;
+
+	return hre_err;
+}
+
+int hre_verify_program(struct key_program *prg)
+{
+	uint32_t crc;
+
+	crc = crc32(0, prg->code, prg->code_size);
+
+	if (crc != prg->code_crc) {
+		printf("HRC crc mismatch: %08x != %08x\n",
+		       crc, prg->code_crc);
+		return 1;
+	}
+	return 0;
+}
diff --git a/board/gdsys/a38x/hre.h b/board/gdsys/a38x/hre.h
new file mode 100644
index 0000000..84ce279
--- /dev/null
+++ b/board/gdsys/a38x/hre.h
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 2013
+ * Reinhard Pfau, Guntermann & Drunck GmbH, reinhard.pfau@gdsys.cc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __HRE_H
+#define __HRE_H
+
+struct key_program {
+	uint32_t magic;
+	uint32_t code_crc;
+	uint32_t code_size;
+	uint8_t code[];
+};
+
+struct h_reg {
+	bool valid;
+	uint8_t digest[20];
+};
+
+/* CCDM specific contants */
+enum {
+	/* NV indices */
+	NV_COMMON_DATA_INDEX	= 0x40000001,
+	/* magics for key blob chains */
+	MAGIC_KEY_PROGRAM	= 0x68726500,
+	MAGIC_HMAC		= 0x68616300,
+	MAGIC_END_OF_CHAIN	= 0x00000000,
+	/* sizes */
+	NV_COMMON_DATA_MIN_SIZE	= 3 * sizeof(uint64_t) + 2 * sizeof(uint16_t),
+};
+
+int hre_verify_program(struct key_program *prg);
+int hre_run_program(const uint8_t *code, size_t code_size);
+
+#endif /* __HRE_H */
diff --git a/board/gdsys/a38x/hydra.c b/board/gdsys/a38x/hydra.c
new file mode 100644
index 0000000..fa50ad2
--- /dev/null
+++ b/board/gdsys/a38x/hydra.c
@@ -0,0 +1,138 @@
+#include <common.h>
+#include <console.h> /* ctrlc */
+#include <asm/io.h>
+
+#include "hydra.h"
+
+enum {
+	HWVER_100 = 0,
+	HWVER_110 = 1,
+	HWVER_120 = 2,
+};
+
+static struct pci_device_id hydra_supported[] = {
+	{ 0x6d5e, 0xcdc1 },
+	{}
+};
+
+static struct ihs_fpga *fpga;
+
+struct ihs_fpga *get_fpga(void)
+{
+	return fpga;
+}
+
+void print_hydra_version(uint index)
+{
+	u32 versions = readl(&fpga->versions);
+	u32 fpga_version = readl(&fpga->fpga_version);
+
+	uint hardware_version = versions & 0xf;
+
+	printf("FPGA%u: mapped to %p\n       ", index, fpga);
+
+	switch (hardware_version) {
+	case HWVER_100:
+		printf("HW-Ver 1.00\n");
+		break;
+
+	case HWVER_110:
+		printf("HW-Ver 1.10\n");
+		break;
+
+	case HWVER_120:
+		printf("HW-Ver 1.20\n");
+		break;
+
+	default:
+		printf("HW-Ver %d(not supported)\n",
+		       hardware_version);
+		break;
+	}
+
+	printf("       FPGA V %d.%02d\n",
+	       fpga_version / 100, fpga_version % 100);
+}
+
+void hydra_initialize(void)
+{
+	uint i;
+	pci_dev_t devno;
+
+	/* Find and probe all the matching PCI devices */
+	for (i = 0; (devno = pci_find_devices(hydra_supported, i)) >= 0; i++) {
+		u32 val;
+
+		/* Try to enable I/O accesses and bus-mastering */
+		val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+		pci_write_config_dword(devno, PCI_COMMAND, val);
+
+		/* Make sure it worked */
+		pci_read_config_dword(devno, PCI_COMMAND, &val);
+		if (!(val & PCI_COMMAND_MEMORY)) {
+			puts("Can't enable I/O memory\n");
+			continue;
+		}
+		if (!(val & PCI_COMMAND_MASTER)) {
+			puts("Can't enable bus-mastering\n");
+			continue;
+		}
+
+		/* read FPGA details */
+		fpga = pci_map_bar(devno, PCI_BASE_ADDRESS_0,
+				   PCI_REGION_MEM);
+
+		print_hydra_version(i);
+	}
+}
+
+#define REFL_PATTERN (0xdededede)
+#define REFL_PATTERN_INV (~REFL_PATTERN)
+
+int do_hydrate(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	uint k = 0;
+	void __iomem *pcie2_base = (void __iomem *)(MVEBU_REG_PCIE_BASE +
+						    0x4000);
+
+	if (!fpga)
+		return -1;
+
+	while (1) {
+		u32 res;
+
+		writel(REFL_PATTERN, &fpga->reflection_low);
+		res = readl(&fpga->reflection_low);
+		if (res != REFL_PATTERN_INV)
+			printf("round %u: read %08x, expected %08x\n",
+			       k, res, REFL_PATTERN_INV);
+		writel(REFL_PATTERN_INV, &fpga->reflection_low);
+		res = readl(&fpga->reflection_low);
+		if (res != REFL_PATTERN)
+			printf("round %u: read %08x, expected %08x\n",
+			       k, res, REFL_PATTERN);
+
+		res = readl(pcie2_base + 0x118) & 0x1f;
+		if (res)
+			printf("FrstErrPtr %u\n", res);
+		res = readl(pcie2_base + 0x104);
+		if (res) {
+			printf("Uncorrectable Error Status 0x%08x\n", res);
+			writel(res, pcie2_base + 0x104);
+		}
+
+		if (!(++k % 10000))
+			printf("round %u\n", k);
+
+		if (ctrlc())
+			break;
+	}
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	hydrate,	1,	0,	do_hydrate,
+	"hydra reflection test",
+	"hydra reflection test"
+);
diff --git a/board/gdsys/a38x/hydra.h b/board/gdsys/a38x/hydra.h
new file mode 100644
index 0000000..26562a5
--- /dev/null
+++ b/board/gdsys/a38x/hydra.h
@@ -0,0 +1,14 @@
+struct ihs_fpga {
+	u32 reflection_low;		/* 0x0000 */
+	u32 versions;			/* 0x0004 */
+	u32 fpga_version;		/* 0x0008 */
+	u32 fpga_features;		/* 0x000c */
+	u32 reserved0[4];		/* 0x0010 */
+	u32 control;			/* 0x0020 */
+	u32 reserved1[375];		/* 0x0024 */
+	u32 qsgmii_port_state[80];	/* 0x0600 */
+};
+
+void print_hydra_version(uint index);
+void hydra_initialize(void);
+struct ihs_fpga *get_fpga(void);
diff --git a/board/gdsys/a38x/ihs_phys.c b/board/gdsys/a38x/ihs_phys.c
new file mode 100644
index 0000000..494de18
--- /dev/null
+++ b/board/gdsys/a38x/ihs_phys.c
@@ -0,0 +1,355 @@
+#include <common.h>
+#include <dm.h>
+#include <miiphy.h>
+#include <asm-generic/gpio.h>
+
+#include "ihs_phys.h"
+#include "dt_helpers.h"
+
+enum {
+	PORTTYPE_MAIN_CAT,
+	PORTTYPE_TOP_CAT,
+	PORTTYPE_16C_16F,
+	PORTTYPE_UNKNOWN
+};
+
+static struct porttype {
+	bool phy_invert_in_pol;
+	bool phy_invert_out_pol;
+} porttypes[] = {
+	{ true, false },
+	{ false, true },
+	{ false, false },
+};
+
+static void ihs_phy_config(struct phy_device *phydev, bool qinpn, bool qoutpn)
+{
+	u16 reg;
+
+	phy_config(phydev);
+
+	/* enable QSGMII autonegotiation with flow control */
+	phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0004);
+	reg = phy_read(phydev, MDIO_DEVAD_NONE, 16);
+	reg |= (3 << 6);
+	phy_write(phydev, MDIO_DEVAD_NONE, 16, reg);
+
+	/*
+	 * invert QSGMII Q_INP/N and Q_OUTP/N if required
+	 * and perform global reset
+	 */
+	reg = phy_read(phydev, MDIO_DEVAD_NONE, 26);
+	if (qinpn)
+		reg |= (1 << 13);
+	if (qoutpn)
+		reg |= (1 << 12);
+	reg |= (1 << 15);
+	phy_write(phydev, MDIO_DEVAD_NONE, 26, reg);
+
+	/* advertise 1000BASE-T full-duplex only  */
+	phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x0000);
+	reg = phy_read(phydev, MDIO_DEVAD_NONE, 4);
+	reg &= ~0x1e0;
+	phy_write(phydev, MDIO_DEVAD_NONE, 4, reg);
+	reg = phy_read(phydev, MDIO_DEVAD_NONE, 9);
+	reg = (reg & ~0x300) | 0x200;
+	phy_write(phydev, MDIO_DEVAD_NONE, 9, reg);
+
+	/* copper power up */
+	reg = phy_read(phydev, MDIO_DEVAD_NONE, 16);
+	reg &= ~0x0004;
+	phy_write(phydev, MDIO_DEVAD_NONE, 16, reg);
+}
+
+uint calculate_octo_phy_mask(void)
+{
+	uint k;
+	uint octo_phy_mask = 0;
+	struct gpio_desc gpio = {};
+	char gpio_name[64];
+	static const char * const dev_name[] = {"pca9698@23", "pca9698@21",
+						"pca9698@24", "pca9698@25",
+						"pca9698@26"};
+
+	/* mark all octo phys that should be present */
+	for (k = 0; k < 5; ++k) {
+		snprintf(gpio_name, 64, "cat-gpio-%u", k);
+
+		if (request_gpio_by_name(&gpio, dev_name[k], 0x20, gpio_name))
+			continue;
+
+		/* check CAT flag */
+		if (dm_gpio_get_value(&gpio))
+			octo_phy_mask |= (1 << (k * 2));
+		else
+			/* If CAT == 0, there's no second octo phy -> skip */
+			continue;
+
+		snprintf(gpio_name, 64, "second-octo-gpio-%u", k);
+
+		if (request_gpio_by_name(&gpio, dev_name[k], 0x27, gpio_name)) {
+			/* default: second octo phy is present */
+			octo_phy_mask |= (1 << (k * 2 + 1));
+			continue;
+		}
+
+		if (dm_gpio_get_value(&gpio) == 0)
+			octo_phy_mask |= (1 << (k * 2 + 1));
+	}
+
+	return octo_phy_mask;
+}
+
+int register_miiphy_bus(uint k, struct mii_dev **bus)
+{
+	int retval;
+	struct mii_dev *mdiodev = mdio_alloc();
+	char *name = bb_miiphy_buses[k].name;
+
+	if (!mdiodev)
+		return -ENOMEM;
+	strncpy(mdiodev->name,
+		name,
+		MDIO_NAME_LEN);
+	mdiodev->read = bb_miiphy_read;
+	mdiodev->write = bb_miiphy_write;
+
+	retval = mdio_register(mdiodev);
+	if (retval < 0)
+		return retval;
+	*bus = miiphy_get_dev_by_name(name);
+
+	return 0;
+}
+
+struct porttype *get_porttype(uint octo_phy_mask, uint k)
+{
+	uint octo_index = k * 4;
+
+	if (!k) {
+		if (octo_phy_mask & 0x01)
+			return &porttypes[PORTTYPE_MAIN_CAT];
+		else if (!(octo_phy_mask & 0x03))
+			return &porttypes[PORTTYPE_16C_16F];
+	} else {
+		if (octo_phy_mask & (1 << octo_index))
+			return &porttypes[PORTTYPE_TOP_CAT];
+	}
+
+	return NULL;
+}
+
+int init_single_phy(struct porttype *porttype, struct mii_dev *bus,
+		    uint bus_idx, uint m, uint phy_idx)
+{
+	struct phy_device *phydev = phy_find_by_mask(
+		bus, 1 << (m * 8 + phy_idx),
+		PHY_INTERFACE_MODE_MII);
+
+	printf(" %u", bus_idx * 32 + m * 8 + phy_idx);
+
+	if (!phydev)
+		puts("!");
+	else
+		ihs_phy_config(phydev, porttype->phy_invert_in_pol,
+			       porttype->phy_invert_out_pol);
+
+	return 0;
+}
+
+int init_octo_phys(uint octo_phy_mask)
+{
+	uint bus_idx;
+
+	/* there are up to four octo-phys on each mdio bus */
+	for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; ++bus_idx) {
+		uint m;
+		uint octo_index = bus_idx * 4;
+		struct mii_dev *bus = NULL;
+		struct porttype *porttype = NULL;
+		int ret;
+
+		porttype = get_porttype(octo_phy_mask, bus_idx);
+
+		if (!porttype)
+			continue;
+
+		for (m = 0; m < 4; ++m) {
+			uint phy_idx;
+
+			/**
+			 * Register a bus device if there is at least one phy
+			 * on the current bus
+			 */
+			if (!m && octo_phy_mask & (0xf << octo_index)) {
+				ret = register_miiphy_bus(bus_idx, &bus);
+				if (ret)
+					return ret;
+			}
+
+			if (!(octo_phy_mask & BIT(octo_index + m)))
+				continue;
+
+			for (phy_idx = 0; phy_idx < 8; ++phy_idx)
+				init_single_phy(porttype, bus, bus_idx, m,
+						phy_idx);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * MII GPIO bitbang implementation
+ * MDC MDIO bus
+ * 13  14   PHY1-4
+ * 25  45   PHY5-8
+ * 46  24   PHY9-10
+ */
+
+struct gpio_mii {
+	int index;
+	struct gpio_desc mdc_gpio;
+	struct gpio_desc mdio_gpio;
+	int mdc_num;
+	int mdio_num;
+	int mdio_value;
+} gpio_mii_set[] = {
+	{ 0, {}, {}, 13, 14, 1 },
+	{ 1, {}, {}, 25, 45, 1 },
+	{ 2, {}, {}, 46, 24, 1 },
+};
+
+static int mii_mdio_init(struct bb_miiphy_bus *bus)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+	char name[32] = {};
+	struct udevice *gpio_dev1 = NULL;
+	struct udevice *gpio_dev2 = NULL;
+
+	if (uclass_get_device_by_name(UCLASS_GPIO, "gpio@18100", &gpio_dev1) ||
+	    uclass_get_device_by_name(UCLASS_GPIO, "gpio@18140", &gpio_dev2)) {
+		printf("Could not get GPIO device.\n");
+		return 1;
+	}
+
+	if (gpio_mii->mdc_num > 31) {
+		gpio_mii->mdc_gpio.dev = gpio_dev2;
+		gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num - 32;
+	} else {
+		gpio_mii->mdc_gpio.dev = gpio_dev1;
+		gpio_mii->mdc_gpio.offset = gpio_mii->mdc_num;
+	}
+	gpio_mii->mdc_gpio.flags = 0;
+	snprintf(name, 32, "bb_miiphy_bus-%d-mdc", gpio_mii->index);
+	dm_gpio_request(&gpio_mii->mdc_gpio, name);
+
+	if (gpio_mii->mdio_num > 31) {
+		gpio_mii->mdio_gpio.dev = gpio_dev2;
+		gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num - 32;
+	} else {
+		gpio_mii->mdio_gpio.dev = gpio_dev1;
+		gpio_mii->mdio_gpio.offset = gpio_mii->mdio_num;
+	}
+	gpio_mii->mdio_gpio.flags = 0;
+	snprintf(name, 32, "bb_miiphy_bus-%d-mdio", gpio_mii->index);
+	dm_gpio_request(&gpio_mii->mdio_gpio, name);
+
+	dm_gpio_set_dir_flags(&gpio_mii->mdc_gpio, GPIOD_IS_OUT);
+	dm_gpio_set_value(&gpio_mii->mdc_gpio, 1);
+
+	return 0;
+}
+
+static int mii_mdio_active(struct bb_miiphy_bus *bus)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+
+	dm_gpio_set_value(&gpio_mii->mdc_gpio, gpio_mii->mdio_value);
+
+	return 0;
+}
+
+static int mii_mdio_tristate(struct bb_miiphy_bus *bus)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+
+	dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN);
+
+	return 0;
+}
+
+static int mii_set_mdio(struct bb_miiphy_bus *bus, int v)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+
+	dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_OUT);
+	dm_gpio_set_value(&gpio_mii->mdio_gpio, v);
+	gpio_mii->mdio_value = v;
+
+	return 0;
+}
+
+static int mii_get_mdio(struct bb_miiphy_bus *bus, int *v)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+
+	dm_gpio_set_dir_flags(&gpio_mii->mdio_gpio, GPIOD_IS_IN);
+	*v = (dm_gpio_get_value(&gpio_mii->mdio_gpio));
+
+	return 0;
+}
+
+static int mii_set_mdc(struct bb_miiphy_bus *bus, int v)
+{
+	struct gpio_mii *gpio_mii = bus->priv;
+
+	dm_gpio_set_value(&gpio_mii->mdc_gpio, v);
+
+	return 0;
+}
+
+static int mii_delay(struct bb_miiphy_bus *bus)
+{
+	udelay(1);
+
+	return 0;
+}
+
+struct bb_miiphy_bus bb_miiphy_buses[] = {
+	{
+		.name = "ihs0",
+		.init = mii_mdio_init,
+		.mdio_active = mii_mdio_active,
+		.mdio_tristate = mii_mdio_tristate,
+		.set_mdio = mii_set_mdio,
+		.get_mdio = mii_get_mdio,
+		.set_mdc = mii_set_mdc,
+		.delay = mii_delay,
+		.priv = &gpio_mii_set[0],
+	},
+	{
+		.name = "ihs1",
+		.init = mii_mdio_init,
+		.mdio_active = mii_mdio_active,
+		.mdio_tristate = mii_mdio_tristate,
+		.set_mdio = mii_set_mdio,
+		.get_mdio = mii_get_mdio,
+		.set_mdc = mii_set_mdc,
+		.delay = mii_delay,
+		.priv = &gpio_mii_set[1],
+	},
+	{
+		.name = "ihs2",
+		.init = mii_mdio_init,
+		.mdio_active = mii_mdio_active,
+		.mdio_tristate = mii_mdio_tristate,
+		.set_mdio = mii_set_mdio,
+		.get_mdio = mii_get_mdio,
+		.set_mdc = mii_set_mdc,
+		.delay = mii_delay,
+		.priv = &gpio_mii_set[2],
+	},
+};
+
+int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
diff --git a/board/gdsys/a38x/ihs_phys.h b/board/gdsys/a38x/ihs_phys.h
new file mode 100644
index 0000000..c4bec4d
--- /dev/null
+++ b/board/gdsys/a38x/ihs_phys.h
@@ -0,0 +1,2 @@
+uint calculate_octo_phy_mask(void);
+int init_octo_phys(uint octo_phy_mask);
diff --git a/board/gdsys/a38x/keyprogram.c b/board/gdsys/a38x/keyprogram.c
new file mode 100644
index 0000000..a4a6f1c
--- /dev/null
+++ b/board/gdsys/a38x/keyprogram.c
@@ -0,0 +1,158 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <tpm.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+
+#include "hre.h"
+
+int flush_keys(void)
+{
+	u16 key_count;
+	u8 buf[288];
+	u8 *ptr;
+	u32 err;
+	uint i;
+
+	/* fetch list of already loaded keys in the TPM */
+	err = tpm_get_capability(TPM_CAP_HANDLE, TPM_RT_KEY, buf, sizeof(buf));
+	if (err)
+		return -1;
+	key_count = get_unaligned_be16(buf);
+	ptr = buf + 2;
+	for (i = 0; i < key_count; ++i, ptr += 4) {
+		err = tpm_flush_specific(get_unaligned_be32(ptr), TPM_RT_KEY);
+		if (err && err != TPM_KEY_OWNER_CONTROL)
+			return err;
+	}
+
+	return 0;
+}
+
+int decode_hexstr(char *hexstr, u8 **result)
+{
+	int len = strlen(hexstr);
+	int bytes = len / 2;
+	int i;
+	u8 acc = 0;
+
+	if (len % 2 == 1)
+		return 1;
+
+	*result = (u8 *)malloc(bytes);
+
+	for (i = 0; i < len; i++) {
+		char cur = tolower(hexstr[i]);
+		u8 val;
+
+		if ((cur >= 'a' && cur <= 'f') || (cur >= '0' && cur <= '9')) {
+			val = cur - (cur > '9' ? 87 : 48);
+
+			if (i % 2 == 0)
+				acc = 16 * val;
+			else
+				(*result)[i / 2] = acc + val;
+		} else {
+			free(*result);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+int extract_subprogram(u8 **progdata, u32 expected_magic,
+		       struct key_program **result)
+{
+	struct key_program *prog = *result;
+	u32 magic, code_crc, code_size;
+
+	magic = get_unaligned_be32(*progdata);
+	code_crc = get_unaligned_be32(*progdata + 4);
+	code_size = get_unaligned_be32(*progdata + 8);
+
+	*progdata += 12;
+
+	if (magic != expected_magic)
+		return -1;
+
+	*result = malloc(sizeof(struct key_program) + code_size);
+
+	if (!*result)
+		return -1;
+
+	prog->magic = magic;
+	prog->code_crc = code_crc;
+	prog->code_size = code_size;
+	memcpy(prog->code, *progdata, code_size);
+
+	*progdata += code_size;
+
+	if (hre_verify_program(prog)) {
+		free(prog);
+		return -1;
+	}
+
+	return 0;
+}
+
+struct key_program *parse_and_check_keyprog(u8 *progdata)
+{
+	struct key_program *result = NULL, *hmac = NULL;
+
+	/* Part 1: Load key program */
+
+	if (extract_subprogram(&progdata, MAGIC_KEY_PROGRAM, &result))
+		return NULL;
+
+	/* Part 2: Load hmac program */
+
+	if (extract_subprogram(&progdata, MAGIC_HMAC, &hmac))
+		return NULL;
+
+	free(hmac);
+
+	return result;
+}
+
+int load_and_run_keyprog(void)
+{
+	char *cmd = NULL;
+	u8 *binprog = NULL;
+	char *hexprog;
+	struct key_program *prog;
+
+	cmd = getenv("loadkeyprogram");
+
+	if (!cmd || run_command(cmd, 0))
+		return 1;
+
+	hexprog = getenv("keyprogram");
+
+	if (decode_hexstr(hexprog, &binprog))
+		return 1;
+
+	prog = parse_and_check_keyprog(binprog);
+	free(binprog);
+
+	if (!prog)
+		return 1;
+
+	if (hre_run_program(prog->code, prog->code_size)) {
+		free(prog);
+		return 1;
+	}
+
+	printf("\nSD code ran successfully\n");
+
+	free(prog);
+
+	return 0;
+}
diff --git a/board/gdsys/a38x/keyprogram.h b/board/gdsys/a38x/keyprogram.h
new file mode 100644
index 0000000..a5ea7d3
--- /dev/null
+++ b/board/gdsys/a38x/keyprogram.h
@@ -0,0 +1,14 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __KEYPROGRAM_H
+#define __KEYPROGRAM_H
+
+int load_and_run_keyprog(void);
+int flush_keys(void);
+
+#endif /* __KEYPROGRAM_H */
diff --git a/board/gdsys/a38x/kwbimage.cfg.in b/board/gdsys/a38x/kwbimage.cfg.in
new file mode 100644
index 0000000..72e67d7
--- /dev/null
+++ b/board/gdsys/a38x/kwbimage.cfg.in
@@ -0,0 +1,12 @@
+#
+# Copyright (C) 2014 Stefan Roese <sr@denx.de>
+#
+
+# Armada 38x uses version 1 image format
+VERSION		1
+
+# Boot Media configurations
+#@BOOT_FROM
+
+# Binary Header (bin_hdr) with DDR3 training code
+BINARY spl/u-boot-spl.bin 0000005b 00000068
diff --git a/board/gdsys/a38x/spl.c b/board/gdsys/a38x/spl.c
new file mode 100644
index 0000000..2d05a9c
--- /dev/null
+++ b/board/gdsys/a38x/spl.c
@@ -0,0 +1,21 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/arch/cpu.h>
+
+void spl_board_init(void)
+{
+#if CONFIG_SPL_BOOT_DEVICE == SPL_BOOT_SPI_NOR_FLASH
+	u32 *bootrom_save = (u32 *)CONFIG_SPL_BOOTROM_SAVE;
+	u32 *regs = (u32 *)(*bootrom_save);
+
+	printf("Returning to BootROM (return address %08x)...\n", regs[13]);
+	return_to_bootrom();
+#endif
+}
diff --git a/configs/controlcenterdc_defconfig b/configs/controlcenterdc_defconfig
new file mode 100644
index 0000000..e1c4f0c
--- /dev/null
+++ b/configs/controlcenterdc_defconfig
@@ -0,0 +1,58 @@
+CONFIG_ARM=y
+CONFIG_ARCH_MVEBU=y
+CONFIG_SPL_GPIO_SUPPORT=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_TARGET_CONTROLCENTERDC=y
+CONFIG_SPL_SPI_FLASH_SUPPORT=y
+CONFIG_SPL_SPI_SUPPORT=y
+CONFIG_DEFAULT_DEVICE_TREE="armada-38x-controlcenterdc"
+CONFIG_FIT=y
+CONFIG_FIT_SIGNATURE=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_SPL=y
+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
+CONFIG_HUSH_PARSER=y
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_GO is not set
+# CONFIG_CMD_IMLS is not set
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+CONFIG_CMD_USB=y
+CONFIG_CMD_GPIO=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_TPM=y
+CONFIG_CMD_EXT2=y
+CONFIG_CMD_EXT4=y
+CONFIG_EFI_PARTITION=y
+CONFIG_OF_BOARD_FIXUP=y
+CONFIG_SPL_OF_TRANSLATE=y
+CONFIG_DM_GPIO=y
+CONFIG_DM_PCA953X=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_MV=y
+CONFIG_SPI_FLASH=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_BASE=0xd0012000
+CONFIG_DEBUG_UART_CLOCK=250000000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYS_NS16550=y
+CONFIG_TPM_ATMEL_TWI=y
+CONFIG_TPM_AUTH_SESSIONS=y
+CONFIG_TPM_FLUSH_RESOURCES=y
+CONFIG_USB=y
+CONFIG_DM_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_TPM=y
+# CONFIG_EFI_LOADER is not set
diff --git a/include/configs/controlcenterdc.h b/include/configs/controlcenterdc.h
new file mode 100644
index 0000000..add5f90
--- /dev/null
+++ b/include/configs/controlcenterdc.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ * Copyright (C) 2016 Mario Six <mario.six@gdsys.cc>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _CONFIG_CONTROLCENTERDC_H
+#define _CONFIG_CONTROLCENTERDC_H
+
+/*
+ * High Level Configuration Options (easy to change)
+ */
+#define CONFIG_CUSTOMER_BOARD_SUPPORT
+
+#define CONFIG_SKIP_LOWLEVEL_INIT	/* disable board lowlevel_init */
+#define CONFIG_DISPLAY_BOARDINFO_LATE
+#define CONFIG_BOARD_LATE_INIT
+#define CONFIG_LAST_STAGE_INIT
+#define CONFIG_SPL_BOARD_INIT
+
+/*
+ * TEXT_BASE needs to be below 16MiB, since this area is scrubbed
+ * for DDR ECC byte filling in the SPL before loading the main
+ * U-Boot into it.
+ */
+#define	CONFIG_SYS_TEXT_BASE	0x00800000
+
+#define CONFIG_SYS_TCLK		250000000	/* 250MHz */
+
+#define CONFIG_LOADADDR 		1000000
+
+/*
+ * Commands configuration
+ */
+#define CONFIG_CMD_ENV
+#define CONFIG_CMD_I2C
+#define CONFIG_CMD_PCI
+#define CONFIG_CMD_SCSI
+#define CONFIG_CMD_SPI
+
+/* SPI NOR flash default params, used by sf commands */
+#define CONFIG_SF_DEFAULT_BUS		1
+#define CONFIG_SF_DEFAULT_SPEED		1000000
+#define CONFIG_SF_DEFAULT_MODE		SPI_MODE_3
+
+/*
+ * SDIO/MMC Card Configuration
+ */
+#define CONFIG_SYS_MMC_BASE		MVEBU_SDIO_BASE
+
+/*
+ * SATA/SCSI/AHCI configuration
+ */
+#define CONFIG_LIBATA
+#define CONFIG_SCSI
+#define CONFIG_SCSI_AHCI
+#define CONFIG_SCSI_AHCI_PLAT
+#define CONFIG_SYS_SCSI_MAX_SCSI_ID	2
+#define CONFIG_SYS_SCSI_MAX_LUN		1
+#define CONFIG_SYS_SCSI_MAX_DEVICE	(CONFIG_SYS_SCSI_MAX_SCSI_ID * \
+					 CONFIG_SYS_SCSI_MAX_LUN)
+
+/* Additional FS support/configuration */
+#define CONFIG_SUPPORT_VFAT
+
+/* USB/EHCI configuration */
+#define CONFIG_EHCI_IS_TDI
+
+/* Environment in SPI NOR flash */
+#define CONFIG_ENV_IS_IN_SPI_FLASH
+#define CONFIG_ENV_SPI_BUS		1
+#define CONFIG_ENV_OFFSET		(1 << 20) /* 1MiB in */
+#define CONFIG_ENV_SIZE			(64 << 10) /* 64KiB */
+#define CONFIG_ENV_SECT_SIZE		(256 << 10) /* 256KiB sectors */
+
+#define CONFIG_PHY_MARVELL		/* there is a marvell phy */
+#define PHY_ANEG_TIMEOUT	8000	/* PHY needs a longer aneg time */
+
+/* PCIe support */
+#ifndef CONFIG_SPL_BUILD
+#define CONFIG_PCI
+#define CONFIG_PCI_MVEBU
+#define CONFIG_PCI_PNP
+#define CONFIG_PCI_SCAN_SHOW
+#endif
+
+#define CONFIG_SYS_ALT_MEMTEST
+
+/*
+ * Software (bit-bang) MII driver configuration
+ */
+#define CONFIG_BITBANGMII		/* bit-bang MII PHY management */
+#define CONFIG_BITBANGMII_MULTI
+
+/* SPL */
+/*
+ * Select the boot device here
+ *
+ * Currently supported are:
+ * SPL_BOOT_SPI_NOR_FLASH	- Booting via SPI NOR flash
+ * SPL_BOOT_SDIO_MMC_CARD	- Booting via SDIO/MMC card (partition 1)
+ */
+#define SPL_BOOT_SPI_NOR_FLASH		1
+#define SPL_BOOT_SDIO_MMC_CARD		2
+#define CONFIG_SPL_BOOT_DEVICE		SPL_BOOT_SPI_NOR_FLASH
+
+/* Defines for SPL */
+#define CONFIG_SPL_FRAMEWORK
+#define CONFIG_SPL_SIZE			(160 << 10)
+
+#if defined(CONFIG_SECURED_MODE_IMAGE)
+#define CONFIG_SPL_TEXT_BASE		0x40002614
+#define CONFIG_SPL_MAX_SIZE		(CONFIG_SPL_SIZE - 0x2614)
+#else
+#define CONFIG_SPL_TEXT_BASE		0x40000030
+#define CONFIG_SPL_MAX_SIZE		(CONFIG_SPL_SIZE - 0x30)
+#endif
+
+#define CONFIG_SPL_BSS_START_ADDR	(0x40000000 + CONFIG_SPL_SIZE)
+#define CONFIG_SPL_BSS_MAX_SIZE		(16 << 10)
+
+#ifdef CONFIG_SPL_BUILD
+#define CONFIG_SYS_MALLOC_SIMPLE
+#endif
+
+#define CONFIG_SPL_STACK		(0x40000000 + ((212 - 16) << 10))
+#define CONFIG_SPL_BOOTROM_SAVE		(CONFIG_SPL_STACK + 4)
+
+#define CONFIG_SPL_LIBCOMMON_SUPPORT
+#define CONFIG_SPL_LIBGENERIC_SUPPORT
+#define CONFIG_SPL_SERIAL_SUPPORT
+#define CONFIG_SPL_I2C_SUPPORT
+
+#if CONFIG_SPL_BOOT_DEVICE == SPL_BOOT_SPI_NOR_FLASH
+/* SPL related SPI defines */
+#define CONFIG_SPL_SPI_LOAD
+#define CONFIG_SYS_SPI_U_BOOT_OFFS	0x30000
+#define CONFIG_SYS_U_BOOT_OFFS		CONFIG_SYS_SPI_U_BOOT_OFFS
+#endif
+
+#if CONFIG_SPL_BOOT_DEVICE == SPL_BOOT_SDIO_MMC_CARD
+/* SPL related MMC defines */
+#define CONFIG_SPL_MMC_SUPPORT
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION 1
+#define CONFIG_SYS_MMC_U_BOOT_OFFS		(168 << 10)
+#define CONFIG_SYS_U_BOOT_OFFS			CONFIG_SYS_MMC_U_BOOT_OFFS
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR	(CONFIG_SYS_U_BOOT_OFFS / 512)
+#ifdef CONFIG_SPL_BUILD
+#define CONFIG_FIXED_SDHCI_ALIGNED_BUFFER	0x00180000	/* in SDRAM */
+#endif
+#endif
+
+/*
+ * Environment Configuration
+ */
+#define CONFIG_ENV_OVERWRITE
+
+#define CONFIG_BAUDRATE 115200
+
+#define CONFIG_HOSTNAME		ccdc
+#define CONFIG_ROOTPATH		"/opt/nfsroot"
+#define CONFIG_BOOTFILE		"ccdc.img"
+
+#define CONFIG_PREBOOT		/* enable preboot variable */
+
+#define CONFIG_EXTRA_ENV_SETTINGS						\
+	"netdev=eth1\0"						\
+	"consoledev=ttyS1\0"							\
+	"u-boot=u-boot.bin\0"							\
+	"bootfile_addr=1000000\0"						\
+	"keyprogram_addr=3000000\0"						\
+	"keyprogram_file=keyprogram.img\0"						\
+	"fdtfile=controlcenterdc.dtb\0"						\
+	"load=tftpboot ${loadaddr} ${u-boot}\0"					\
+	"mmcdev=0:2\0"								\
+	"update=sf probe 1:0;"							\
+		" sf erase 0 +${filesize};"					\
+		" sf write ${fileaddr} 0 ${filesize}\0"				\
+	"upd=run load update\0"							\
+	"fdt_high=0x10000000\0"							\
+	"initrd_high=0x10000000\0"						\
+	"loadkeyprogram=tpm flush_keys;"					\
+		" mmc rescan;"							\
+		" ext4load mmc ${mmcdev} ${keyprogram_addr} ${keyprogram_file};"\
+		" source ${keyprogram_addr}:script@1\0"				\
+	"gpio1=gpio@22_25\0"							\
+	"gpio2=A29\0"								\
+	"blinkseq='0 0 0 0 2 0 2 2 3 1 3 1 0 0 2 2 3 1 3 3 2 0 2 2 3 1 1 1 "	\
+		  "2 0 2 2 3 1 3 1 0 0 2 0 3 3 3 1 2 0 0 0 3 1 1 1 0 0 0 0'\0"	\
+	"bootfail=for i in ${blinkseq}; do"					\
+		" if test $i -eq 0; then"					\
+		" gpio clear ${gpio1}; gpio set ${gpio2};"			\
+		" elif test $i -eq 1; then"					\
+		" gpio clear ${gpio1}; gpio clear ${gpio2};"			\
+		" elif test $i -eq 2; then"					\
+		" gpio set ${gpio1}; gpio set ${gpio2};"			\
+		" else;"							\
+		" gpio clear ${gpio1}; gpio set ${gpio2};"			\
+		" fi; sleep 0.12; done\0"
+
+#define CONFIG_NFSBOOTCOMMAND								\
+	"setenv bootargs root=/dev/nfs rw "						\
+	"nfsroot=${serverip}:${rootpath} "						\
+	"ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}:${netdev}:off "	\
+	"console=${consoledev},${baudrate} ${othbootargs}; "				\
+	"tftpboot ${bootfile_addr} ${bootfile}; "						\
+	"bootm ${bootfile_addr}"
+
+#define CONFIG_MMCBOOTCOMMAND					\
+	"setenv bootargs root=/dev/mmcblk0p3 rw rootwait "	\
+	"console=${consoledev},${baudrate} ${othbootargs}; "	\
+	"ext2load mmc 0:2 ${bootfile_addr} ${bootfile}; "	\
+	"bootm ${bootfile_addr}"
+
+#define CONFIG_BOOTCOMMAND			\
+	"if env exists keyprogram; then;"	\
+	" setenv keyprogram; run nfsboot;"	\
+        " fi;"					\
+        " run dobootfail"
+
+/*
+ * mv-common.h should be defined after CMD configs since it used them
+ * to enable certain macros
+ */
+#include "mv-common.h"
+
+#endif /* _CONFIG_CONTROLCENTERDC_H */