aboutsummaryrefslogtreecommitdiffstats
path: root/board/gdsys
diff options
context:
space:
mode:
authorDirk Eibach <dirk.eibach@gdsys.cc>2017-02-22 16:07:23 +0100
committerStefan Roese <sr@denx.de>2017-03-23 15:48:28 +0100
commit60083261a1b33b492ddb5335c125f1223087068b (patch)
treebf90ef4650b59b3b201909db4e95fefb9b377aaf /board/gdsys
parent2a792753d6a1fe8f2310928ebd5534e4d87a8030 (diff)
downloadu-boot-midas-60083261a1b33b492ddb5335c125f1223087068b.tar.gz
u-boot-midas-60083261a1b33b492ddb5335c125f1223087068b.tar.bz2
u-boot-midas-60083261a1b33b492ddb5335c125f1223087068b.zip
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>
Diffstat (limited to 'board/gdsys')
-rw-r--r--board/gdsys/a38x/.gitignore1
-rw-r--r--board/gdsys/a38x/Kconfig36
-rw-r--r--board/gdsys/a38x/MAINTAINERS7
-rw-r--r--board/gdsys/a38x/Makefile44
-rw-r--r--board/gdsys/a38x/controlcenterdc.c279
-rw-r--r--board/gdsys/a38x/dt_helpers.c43
-rw-r--r--board/gdsys/a38x/dt_helpers.h16
-rw-r--r--board/gdsys/a38x/hre.c516
-rw-r--r--board/gdsys/a38x/hre.h38
-rw-r--r--board/gdsys/a38x/hydra.c138
-rw-r--r--board/gdsys/a38x/hydra.h14
-rw-r--r--board/gdsys/a38x/ihs_phys.c355
-rw-r--r--board/gdsys/a38x/ihs_phys.h2
-rw-r--r--board/gdsys/a38x/keyprogram.c158
-rw-r--r--board/gdsys/a38x/keyprogram.h14
-rw-r--r--board/gdsys/a38x/kwbimage.cfg.in12
-rw-r--r--board/gdsys/a38x/spl.c21
17 files changed, 1694 insertions, 0 deletions
diff --git a/board/gdsys/a38x/.gitignore b/board/gdsys/a38x/.gitignore
new file mode 100644
index 0000000000..775b9346b8
--- /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 0000000000..3fdef64b59
--- /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 0000000000..3cb9b63ff0
--- /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 0000000000..e1f0bd8433
--- /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 0000000000..f0efb53447
--- /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 0000000000..759d82a728
--- /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 0000000000..1b95262c51
--- /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 0000000000..1689d44db6
--- /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 0000000000..84ce2794fa
--- /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 0000000000..fa50ad2830
--- /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 0000000000..26562a560a
--- /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 0000000000..494de18f86
--- /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 0000000000..c4bec4d46c
--- /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 0000000000..a4a6f1cca5
--- /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 0000000000..a5ea7d3137
--- /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 0000000000..72e67d75c3
--- /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 0000000000..2d05a9c98a
--- /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
+}