soc/cavium: Integrate BDK files into coreboot

* Make it compile.
* Fix whitespace errors.
* Fix printf formats.
* Add missing headers includes
* Guard headers with ifdefs

Compile DRAM init code in romstage.
Compile QLM, PCIe, RNG, PHY, GPIO, MDIO init code in ramstage.

Change-Id: I0a93219a14bfb6ebe41103a825d5032b11e7f2c6
Signed-off-by: David Hendricks <dhendricks@fb.com>
Reviewed-on: https://review.coreboot.org/25089
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/vendorcode/Makefile.inc b/src/vendorcode/Makefile.inc
index 440972f..522d415 100644
--- a/src/vendorcode/Makefile.inc
+++ b/src/vendorcode/Makefile.inc
@@ -2,3 +2,4 @@
 subdirs-y += google
 subdirs-y += intel
 subdirs-y += siemens
+subdirs-y += cavium
diff --git a/src/vendorcode/cavium/Kconfig b/src/vendorcode/cavium/Kconfig
new file mode 100644
index 0000000..8037762
--- /dev/null
+++ b/src/vendorcode/cavium/Kconfig
@@ -0,0 +1,70 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright 2017-present Facebook, Inc.
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; version 2 of the License.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+config CAVIUM_BDK
+	def_bool n
+	select HAVE_DEBUG_RAM_SETUP
+	help
+	  Build Cavium's BDK in romstage.
+
+if CAVIUM_BDK
+
+menu "BDK"
+
+config CAVIUM_BDK_VERBOSE_INIT
+	bool "Enable verbose init"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose init code.
+
+config CAVIUM_BDK_VERBOSE_DRAM
+	bool "Enable verbose dram init"
+	default y if DEBUG_RAM_SETUP
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose dram init code.
+
+config CAVIUM_BDK_VERBOSE_DRAM_TEST
+	bool "Enable verbose raminit tests"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose DRAM testing code.
+
+config CAVIUM_BDK_VERBOSE_QLM
+	bool "Enable verbose qlm init"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose QLM code.
+
+config CAVIUM_BDK_VERBOSE_PCIE_CONFIG
+	bool "Enable verbose pcie config"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose PCIe config code.
+
+config CAVIUM_BDK_VERBOSE_PCIE
+	bool "Enable verbose pcie init"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose PCIe code.
+
+config CAVIUM_BDK_VERBOSE_PHY
+	bool "Enable verbose phy init"
+	depends on CAVIUM_BDK
+	help
+	  Build Cavium's BDK with verbose PHY code.
+endmenu
+
+endif
diff --git a/src/vendorcode/cavium/Makefile.inc b/src/vendorcode/cavium/Makefile.inc
new file mode 100644
index 0000000..855b3c6
--- /dev/null
+++ b/src/vendorcode/cavium/Makefile.inc
@@ -0,0 +1,92 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright 2017-present Facebook, Inc.
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; version 2 of the License.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+ifeq ($(CONFIG_CAVIUM_BDK),y)
+
+romstage-y += bdk/libbdk-arch/bdk-csr.c
+romstage-y += bdk/libbdk-arch/bdk-model.c
+romstage-y += bdk/libbdk-arch/bdk-numa.c
+romstage-y += bdk/libbdk-boot/bdk-boot-status.c
+romstage-y += bdk/libbdk-dram/bdk-dram-address.c
+romstage-y += bdk/libbdk-dram/bdk-dram-config.c
+romstage-y += bdk/libbdk-dram/bdk-dram-size.c
+romstage-y += bdk/libbdk-dram/bdk-dram-test.c
+romstage-y += bdk/libbdk-dram/bdk-dram-test-addrbus.c
+romstage-y += bdk/libbdk-dram/bdk-dram-test-databus.c
+romstage-y += bdk/libbdk-dram/bdk-dram-test-fastscan.c
+romstage-y += bdk/libbdk-dram/bdk-dram-test-patfil.c
+romstage-y += bdk/libbdk-driver/bdk-driver-rnm.c
+romstage-y += bdk/libbdk-hal/bdk-clock.c
+romstage-y += bdk/libbdk-hal/bdk-config.c
+romstage-y += bdk/libbdk-hal/bdk-gpio.c
+romstage-y += bdk/libbdk-hal/bdk-l2c.c
+romstage-y += bdk/libbdk-os/bdk-init.c
+romstage-y += bdk/libbdk-trust/bdk-trust.c
+romstage-y += bdk/libdram/dram-env.c
+romstage-y += bdk/libdram/dram-init-ddr3.c
+romstage-y += bdk/libdram/dram-l2c.c
+romstage-y += bdk/libdram/dram-spd.c
+romstage-y += bdk/libdram/dram-tune-ddr3.c
+romstage-y += bdk/libdram/lib_octeon_shared.c
+romstage-y += bdk/libdram/libdram.c
+romstage-y += bdk/libdram/libdram-config-load.c
+romstage-y += bdk/libbdk-hal/bdk-access.c
+
+# FIXME: Get rid of lame_string.c
+romstage-y += bdk/lame_string.c
+
+CPPFLAGS_common += -Isrc/vendorcode/cavium/include/bdk
+
+# For bdk_dram_get_size_mbytes()
+ramstage-y += bdk/libbdk-dram/bdk-dram-size.c
+
+ramstage-y += bdk/libbdk-hal/bdk-config.c
+ramstage-y += bdk/libbdk-hal/bdk-qlm.c
+ramstage-y += bdk/libbdk-hal/bdk-pcie-cn8xxx.c
+ramstage-y += bdk/libbdk-hal/bdk-pcie.c
+ramstage-y += bdk/libbdk-hal/bdk-gpio.c
+ramstage-y += bdk/libbdk-hal/bdk-ecam-io.c
+ramstage-y += bdk/libbdk-hal/bdk-usb.c
+ramstage-y += bdk/libbdk-hal/bdk-access.c
+
+ramstage-y += bdk/libbdk-arch/bdk-csr.c
+ramstage-y += bdk/libbdk-arch/bdk-model.c
+ramstage-y += bdk/libbdk-arch/bdk-numa.c
+ramstage-y += bdk/libbdk-hal/qlm/bdk-qlm-common.c
+ramstage-y += bdk/libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.c
+ramstage-y += bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c
+ramstage-y += bdk/libbdk-hal/qlm/bdk-qlm-margin-cn8xxx.c
+
+ramstage-y += bdk/libbdk-boot/bdk-boot-qlm.c
+ramstage-y += bdk/libbdk-boot/bdk-boot-pcie.c
+ramstage-y += bdk/libbdk-boot/bdk-boot-usb.c
+ramstage-y += bdk/libbdk-boot/bdk-boot-gpio.c
+ramstage-y += bdk/libbdk-boot/bdk-boot.c
+
+ramstage-y += bdk/libbdk-hal/if/bdk-if-phy.c
+ramstage-y += bdk/libbdk-hal/if/bdk-if-phy-marvell.c
+ramstage-y += bdk/libbdk-hal/if/bdk-if-phy-vetesse-8514.c
+ramstage-y += bdk/libbdk-hal/if/bdk-if-phy-vetesse.c
+ramstage-y += bdk/libbdk-driver/bdk-driver-mdio.c
+ramstage-y += bdk/libbdk-driver/bdk-driver-rnm.c
+ramstage-y += bdk/libbdk-hal/device/bdk-device.c
+ramstage-y += bdk/libbdk-hal/bdk-ecam.c
+
+# FIXME: Get rid of lame_string.c
+ramstage-y += bdk/lame_string.c
+
+ramstage-$(CONFIG_SOC_CAVIUM_CN81XX) += bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c
+
+endif
diff --git a/src/vendorcode/cavium/bdk/lame_string.c b/src/vendorcode/cavium/bdk/lame_string.c
new file mode 100644
index 0000000..11c5add
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/lame_string.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+ * reserved.
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * string.c: hastily cobbled-together string functions
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <lame_string.h>
+
+static int char_to_val(char c)
+{
+	if (c >= '0' && c <= '9')	/* digits */
+		return c - '0';
+	if (c >= 'A' && c <= 'F')	/* uppercase */
+		return c - 'A' + 10;
+	if (c >= 'a' && c <= 'f')	/* lowercase */
+		return c - 'a' + 10;
+	return -1;
+}
+
+unsigned long long int strtoull(const char *nptr, char **endptr, int base)
+{
+	unsigned long long int val;
+	size_t i, error = 0;
+
+	/* TODO: enforce lameness of this API for now... */
+	assert((base == 0) || (base == 16) || base == 10);
+
+	if (!nptr)
+		return 0;
+
+	/* Trim whitespace */
+	for (i = 0; i < strlen(nptr); i++)
+		if (nptr[i] != ' ')
+			break;
+
+	if (base == 0) {
+		/* Autodetect base */
+		if (strlen(&nptr[i]) >= 2 && ((nptr[i] == '0') &&
+		    ((nptr[i + 1] == 'x') || (nptr[i + 1] == 'X')))) {
+			base = 16;
+			i += 2;	/* start loop after prefix */
+		} else
+			base = 10;
+	}
+
+	val = 0;
+	for (; i < strlen(nptr); i++) {
+		if (base == 16) {
+			if (!isxdigit(nptr[i])) {
+				if (*endptr)
+					*endptr = (char *)&nptr[i];
+				error = 1;
+				break;
+			}
+		} else {
+			if (!isdigit(nptr[i])) {
+				if (*endptr)
+					*endptr = (char *)&nptr[i];
+				error = 1;
+				break;
+			}
+		}
+
+		val *= base;
+		val += char_to_val(nptr[i]);
+	}
+
+	if (error) {
+		printk(BIOS_ERR, "Failed to convert string '%s', base %d to "
+		       "int\n", nptr, base);
+		return 0;
+	}
+	return val;
+}
+
+unsigned long int strtoul(const char *nptr, char **endptr, int base)
+{
+	unsigned long long int u = strtol(nptr, endptr, base);
+	/* FIXME: check for overflow (u > max) */
+	return (unsigned long int)u;
+}
+
+long int strtol(const char *nptr, char **endptr, int base)
+{
+	unsigned long long int u;
+	int is_neg = 0;
+	const char *p;
+	long int ret;
+
+	if (nptr[0] == '-') {
+		is_neg = 1;
+		p = &nptr[1];
+	} else {
+		p = &nptr[0];
+	}
+	u = strtoull(p, NULL, base);
+	/* FIXME: check for overflow (u > max) */
+	if (is_neg)
+		ret = 0 - (long int)u;
+	else
+		ret = (long int)u;
+	return ret;
+}
+
+long long int strtoll(const char *nptr, char **endptr, int base)
+{
+	unsigned long long int u;
+	int is_neg = 0;
+	const char *p;
+	long long int ret;
+
+	if (nptr[0] == '-') {
+		is_neg = 1;
+		p = &nptr[1];
+	} else {
+		p = &nptr[0];
+	}
+	u = strtoull(p, NULL, base);
+	/* FIXME: check for overflow (sign-bit set) */
+	if (is_neg)
+		ret = 0 - (long long int)u;
+	else
+		ret = (long long int)u;
+	return ret;
+}
+
+/* FIXME: replace sscanf() usage for bdk_config_get_int. returns number of
+ * strings converted, so 1 if successful and 0 if not */
+int str_to_int(const char *str, int64_t *val)
+{
+	*val = strtol(str, NULL, 10);
+	return 1;
+}
+
+/* FIXME: replace sscanf() usage for bdk_config_get_int. returns number of
+ * strings converted, so 1 if successful and 0 if not */
+int str_to_hex(const char *str, int64_t *val)
+{
+	*val = strtol(str, NULL, 16);
+	return 1;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-csr.c b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-csr.c
index 981ad23..fc9ac35 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-csr.c
+++ b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-csr.c
@@ -37,9 +37,10 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include <stdio.h>
 #include "libbdk-arch/bdk-csrs-pccpf.h"
 #include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
+#include "libbdk-hal/bdk-pcie.h"
 
 #ifndef BDK_BUILD_HOST
 
@@ -87,9 +88,6 @@
 
         case BDK_CSR_TYPE_PCICONFIGRC:
         {
-            /* Don't allow PCIe register access if PCIe wasn't linked in */
-            if (!bdk_pcie_config_read32)
-                bdk_fatal("PCIe CSR access not supported when PCIe not linked in\n");
             union bdk_pcc_dev_con_s dev_con;
             switch (busnum)
             {
@@ -201,9 +199,6 @@
 
         case BDK_CSR_TYPE_PCICONFIGRC:
         {
-            /* Don't allow PCIe register access if PCIe wasn't linked in */
-            if (!bdk_pcie_config_write32)
-                bdk_fatal("PCIe CSR access not supported when PCIe not linked in\n");
             union bdk_pcc_dev_con_s dev_con;
             switch (busnum)
             {
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-model.c b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-model.c
index f2b4a0c..fc4053e 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-model.c
+++ b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-model.c
@@ -41,6 +41,8 @@
 #include "libbdk-arch/bdk-csrs-mio_fus.h"
 #include "libbdk-arch/bdk-csrs-fus.h"
 #include "libbdk-arch/bdk-csrs-fusf.h"
+#include <libbdk-hal/bdk-clock.h>
+#include <libbdk-hal/bdk-utils.h>
 
 /*
     Format of a SKU
@@ -100,562 +102,6 @@
    6, checking for trusted boot */
 #define FUSES_CHECK_FUSF 0xffff
 
-/***************************************************/
-/* SKU table for t88 */
-/* From "Thunder Part Number fuse overview Rev 16.xlsx" */
-/***************************************************/
-static const model_sku_info_t t88_sku_info[] =
-{
-    /* Index zero reserved for no fuses programmed */
-    { 0x01, "CN", 88, 2601, "AAP", /* 48, 32 cores */
-        { /* List of fuses for this SKU */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x02, "CN", 88, 2601, "AAS", /* 24 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_OCX_DIS,     /* Disable CCPI */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x03, "CN", 88, 2601, "ST", /* 48, 32 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(0), /* Disable PEM0-1 */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(2), /* Disable PEM4-5 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x04, "CN", 88, 2601, "STT", /* 48 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(0), /* Disable PEM0-1 */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(2), /* Disable PEM4-5 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x05, "CN", 88, 2601, "STS", /* 24 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS,     /* Disable LMC2-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_OCX_DIS,     /* Disable CCPI */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(0), /* Disable PEM0-1 */
-            BDK_MIO_FUS_FUSE_NUM_E_PEM_DISX(2), /* Disable PEM4-5 */
-            BDK_MIO_FUS_FUSE_NUM_E_BGX_DISX(1), /* Disable BGX1 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x06, "CN", 88, 2601, "STP", /* 48, 32 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x07, "CN", 88, 2601, "NT", /* 48, 32 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(0),/* Disable SATA0-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(2),/* Disable SATA8-11 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(3),/* Disable SATA12-15 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x08, "CN", 88, 2601, "NTS", /* 24 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS,     /* Disable LMC2-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_OCX_DIS,     /* Disable CCPI */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(0),/* Disable SATA0-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(2),/* Disable SATA8-11 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(3),/* Disable SATA12-15 */
-            BDK_MIO_FUS_FUSE_NUM_E_BGX_DISX(1), /* Disable BGX1 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x09, "CN", 88, 2601, "NTP", /* 48, 32 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(0),/* Disable SATA0-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(1),/* Disable SATA4-7 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(2),/* Disable SATA8-11 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(3),/* Disable SATA12-15 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x0a, "CN", 88, 2601, "CP", /* 48,32 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_NODFA_CP2,   /* Disable HFA */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD134X(0), /* Disable HNA */
-            BDK_MIO_FUS_FUSE_NUM_E_NOZIP,       /* Disable Compression */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(0),/* Disable SATA0-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(2),/* Disable SATA8-11 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(3),/* Disable SATA12-15 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x0b, "CN", 88, 2601, "CPS", /* 24 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_NODFA_CP2,   /* Disable HFA */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD134X(0), /* Disable HNA */
-            BDK_MIO_FUS_FUSE_NUM_E_NOZIP,       /* Disable Compression */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS,     /* Disable LMC2-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_OCX_DIS,     /* Disable CCPI */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(0),/* Disable SATA0-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(2),/* Disable SATA8-11 */
-            BDK_MIO_FUS_FUSE_NUM_E_SATA_DISX(3),/* Disable SATA12-15 */
-            BDK_MIO_FUS_FUSE_NUM_E_BGX_DISX(1), /* Disable BGX1 */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x0c, "CN", 88, 2601, "SNT", /* 48,32 cores, Nitrox connects to PEM2x8, QLM4-5 */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD231X(0), /* Nitrox 3 is present */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x0d, "CN", 88, 2601, "SC", /* 48,32 cores, Nitrox connects to PEM2x8, QLM4-5 */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD231X(0), /* Nitrox 3 is present */
-            BDK_MIO_FUS_FUSE_NUM_E_NODFA_CP2,   /* Disable HFA */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD134X(0), /* Disable HNA */
-            BDK_MIO_FUS_FUSE_NUM_E_NOZIP,       /* Disable Compression */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            0 /* End of fuse list marker */
-        }
-    },
-    /* Index gap for adding more CN88 variants */
-    { 0x20, "CN", 86, 1676, "AAP", /* No part, match unfused CN86XX */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(6), /* Alternate package fuse */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x21, "CN", 86, 1676, "SCP", /* 8 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(6), /* Alternate package fuse */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),/* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_NODFA_CP2,   /* Disable HFA */
-            BDK_MIO_FUS_FUSE_NUM_E_RSVD134X(0), /* Disable HNA */
-            BDK_MIO_FUS_FUSE_NUM_E_NOZIP,       /* Disable Compression */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS,     /* Disable LMC2-3 */
-            BDK_MIO_FUS_FUSE_NUM_E_OCX_DIS,     /* Disable CCPI */
-            BDK_MIO_FUS_FUSE_NUM_E_TNS_CRIPPLE, /* Disable TNS */
-            0 /* End of fuse list marker */
-        }
-    },
-    {} /* End of SKU list marker */
-};
-
-/***************************************************/
-/* SKU table for t83 */
-/* From "Thunder Part Number fuse overview Rev 16.xlsx" */
-/***************************************************/
-static const model_sku_info_t t83_sku_info[] =
-{
-    /* Index zero reserved for no fuses programmed */
-    { 0x01, "CN", 83, 1676, "SCP", /* 24, 20, 16, 12, 8 cores */
-        { /* List of fuses for this SKU */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x02, "CN", 83, 1676, "CP", /* 24, 20, 16, 12, 8 cores */
-        { /* List of fuses for this SKU */
-            /* Disable all Nitrox cores, CPT0 and CPT1 */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(0), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(16), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(17), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(18), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(19), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(20), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(21), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(22), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(23), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(24), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(25), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(26), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(27), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(28), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(29), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(30), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(31), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(32), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(33), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(34), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(35), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(36), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(37), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(38), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(39), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(40), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(41), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(42), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(43), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(44), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(45), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(46), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(47), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(0), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(16), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(17), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(18), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(19), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(20), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(21), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(22), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(23), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x03, "CN", 83, 1676, "AUS", /* 24, 20, 16, 12, 8 cores */
-        { /* List of fuses for this SKU */
-            FUSES_CHECK_FUSF, /* Trusted boot */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x04, "CN", 82, 1676, "SCP", /* 12, 8 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),/* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS, /* Disable upper LMC */
-            /* Disable Nitrox cores CPT0[24-47] and CPT1[12-23] */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(24), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(25), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(26), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(27), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(28), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(29), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(30), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(31), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(32), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(33), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(34), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(35), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(36), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(37), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(38), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(39), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(40), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(41), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(42), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(43), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(44), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(45), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(46), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(47), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(16), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(17), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(18), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(19), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(20), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(21), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(22), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(23), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x05, "CN", 82, 1676, "CP", /* 12, 8 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),/* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_DIS, /* Disable upper LMC */
-            /* Disable all Nitrox cores, CPT0 and CPT1 */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(0), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(16), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(17), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(18), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(19), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(20), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(21), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(22), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(23), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(24), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(25), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(26), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(27), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(28), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(29), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(30), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(31), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(32), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(33), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(34), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(35), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(36), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(37), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(38), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(39), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(40), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(41), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(42), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(43), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(44), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(45), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(46), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT0_ENG_DISX(47), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(0), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(16), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(17), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(18), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(19), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(20), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(21), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(22), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT1_ENG_DISX(23), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    {} /* End of SKU list marker */
-};
-
-/***************************************************/
-/* SKU table for t81 */
-/* From "Thunder Part Number fuse overview Rev 16.xlsx" */
-/***************************************************/
-static const model_sku_info_t t81_sku_info[] =
-{
-    /* Index zero reserved for no fuses programmed */
-    { 0x01, "CN", 81, 676, "SCP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            /* No fuses */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x02, "CN", 81, 676, "CP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(16), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x07, "CN", 81, 676, "AUS", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            FUSES_CHECK_FUSF, /* Trusted boot */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x08, "CN", 81, 676, "AUC", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            FUSES_CHECK_FUSF, /* Trusted boot */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(16), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x03, "CN", 80, 676, "SCP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            /* Note that CHIP_ID(7) is suppose to be blown, but a few chips
-               have incorrect fuses. We allow CN80XX SKUs with or without
-               CHIP_ID(7) */
-            //BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(7),     /* Alternate package fuse 2? */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),    /* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_HALF,        /* LMC is half width */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x04, "CN", 80, 676, "CP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            /* Note that CHIP_ID(7) is suppose to be blown, but a few chips
-               have incorrect fuses. We allow CN80XX SKUs with or without
-               CHIP_ID(7) */
-            //BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(7),     /* Alternate package fuse 2? */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),    /* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_HALF,        /* LMC is half width */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(16), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x05, "CN", 80, 555, "SCP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(6),     /* Alternate package fuse */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),    /* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_HALF,        /* LMC is half width */
-            0 /* End of fuse list marker */
-        }
-    },
-    { 0x06, "CN", 80, 555, "CP", /* 4, 2 cores */
-        { /* List of fuses for this SKU */
-            BDK_MIO_FUS_FUSE_NUM_E_CHIP_IDX(6),     /* Alternate package fuse */
-            BDK_MIO_FUS_FUSE_NUM_E_L2C_CRIPX(1),    /* L2C is half size */
-            BDK_MIO_FUS_FUSE_NUM_E_LMC_HALF,        /* LMC is half width */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(1), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(2), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(3), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(4), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(5), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(6), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(7), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(8), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(9), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(10), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(11), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(12), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(13), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(14), /* Nitrox */
-            //BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(15), /* Nitrox */
-            BDK_MIO_FUS_FUSE_NUM_E_CPT_ENG_DISX(16), /* Nitrox */
-            0 /* End of fuse list marker */
-        }
-    },
-    {} /* End of SKU list marker */
-};
-
-/***************************************************/
-/* SKU table for t93 */
-/***************************************************/
-static const model_sku_info_t t93_sku_info[] =
-{
-    /* Index zero reserved for no fuses programmed */
-    { 0x01, "CN", 93, 1676, "SCP", /* 24, 20, 16, 12, 8 cores */
-        { /* List of fuses for this SKU */
-            /* No fuses */
-            0 /* End of fuse list marker */
-        }
-    },
-    {} /* End of SKU list marker */
-};
-
-/**
- * Given a core count, return the last two digits of a model number
- *
- * @param cores  Number of cores
- *
- * @return Two digit model number
- */
-static int model_digits_for_cores(int cores)
-{
-    /* If the number of cores is between two model levels, use the lower
-       level. This assumes that a model guarantees a minimum number of
-       cores. This should never happen, but you never know */
-    switch (cores)
-    {
-        case  1: return 10; /* CNxx10 = 1 core */
-        case  2: return 20; /* CNxx20 = 2 cores */
-        case  3: return 25; /* CNxx25 = 3 cores */
-        case  4: return 30; /* CNxx30 = 4 cores */
-        case  5: return 32; /* CNxx32 = 5 cores */
-        case  6: return 34; /* CNxx34 = 6 cores */
-        case  7: return 38; /* CNxx38 = 7 cores */
-        case  8: return 40; /* CNxx40 = 8 cores */
-        case  9: return 42; /* CNxx42 = 9 cores */
-        case 10: return 45; /* CNxx45 = 10 cores */
-        case 11: return 48; /* CNxx48 = 11 cores */
-        case 12: return 50; /* CNxx50 = 12 cores */
-        case 13: return 52; /* CNxx52 = 13 cores */
-        case 14: return 55; /* CNxx55 = 14 cores */
-        case 15: return 58; /* CNxx58 = 15 cores */
-        case 16 ... 19: return 60; /* CNxx60 = 16 cores */
-        case 20 ... 23: return 65; /* CNxx65 = 20 cores */
-        case 24 ... 31: return 70; /* CNxx70 = 24 cores */
-        case 32 ... 39: return 80; /* CNxx80 = 32 cores */
-        case 40 ... 43: return 85; /* CNxx85 = 40 cores */
-        case 44 ... 47: return 88; /* CNxx88 = 44 cores */
-        default: return 90; /* CNxx90 = 48 cores */
-    }
-}
-
 /**
  * Return non-zero if the die is in an alternate package. The
  * normal is_model() checks will treat alternate package parts
@@ -694,234 +140,3 @@
     else
         return 0;
 }
-
-/**
- * Return the SKU string for a chip
- *
- * @param node   Node to get SKU for
- *
- * @return Chip's SKU
- */
-const char* bdk_model_get_sku(int node)
-{
-    /* Storage for SKU is per node. Static variable stores the value
-       so we don't decode on every call */
-    static char chip_sku[BDK_NUMA_MAX_NODES][32] = { { 0, }, };
-
-    /* Return the cached string if we've already filled it in */
-    if (chip_sku[node][0])
-        return chip_sku[node];
-
-    /* Figure out which SKU list to use */
-    const model_sku_info_t *sku_info;
-    uint64_t result;
-    asm ("mrs %[rd],MIDR_EL1" : [rd] "=r" (result));
-    result = bdk_extract(result, 4, 12);
-    switch (result)
-    {
-        case 0xa1:
-            sku_info = t88_sku_info;
-            break;
-        case 0xa2:
-            sku_info = t81_sku_info;
-            break;
-        case 0xa3:
-            sku_info = t83_sku_info;
-            break;
-        case 0xb2:
-            sku_info = t93_sku_info;
-            break;
-        default:
-            bdk_fatal("SKU detect: Unknown die\n");
-    }
-
-    /* Read the SKU index from the PNAME fuses */
-    int match_index = -1;
-    // FIXME: Implement PNAME reads
-
-    /* Search the SKU list for the best match, where all the fuses match.
-       Only needed if the PNAME fuses don't specify the index */
-    if (match_index == -1)
-    {
-        match_index = 0;
-        int match_score = -1;
-        int index = 0;
-        while (sku_info[index].fuse_index)
-        {
-            int score = 0;
-            int fuse_index = 0;
-            /* Count the number of fuses that match. A mismatch forces the worst
-               score (-1) */
-            while (sku_info[index].fuses[fuse_index])
-            {
-                int fuse;
-                /* FUSES_CHECK_FUSF is special for trusted parts */
-                if (sku_info[index].fuses[fuse_index] == FUSES_CHECK_FUSF)
-                {
-                    BDK_CSR_INIT(fusf_ctl, node, BDK_FUSF_CTL);
-                    fuse = (fusf_ctl.u >> 6) & 1;
-                }
-                else
-                {
-                    fuse = bdk_fuse_read(node, sku_info[index].fuses[fuse_index]);
-                }
-                if (fuse)
-                {
-                    /* Match, improve the score */
-                    score++;
-                }
-                else
-                {
-                    /* Mismatch, force score bad */
-                    score = -1;
-                    break;
-                }
-                fuse_index++;
-            }
-            /* If this score is better than the last match, use this index as the
-               match */
-            if (score > match_score)
-            {
-                match_score = score;
-                match_index = index;
-            }
-            index++;
-        }
-    }
-
-    /* Use the SKU table to determine the defaults for the SKU parts */
-    const char *prefix          = sku_info[match_index].prefix;
-    int         model           = 100 * sku_info[match_index].model_base;
-    int         cores           = bdk_get_num_cores(node);
-    const char *customer_code   = "";
-    int         rclk_limit      = bdk_clock_get_rate(node, BDK_CLOCK_RCLK) / 1000000;
-    const char *bg_str          = "BG"; /* Default Ball Grid array */
-    int         balls           = sku_info[match_index].num_balls; /* Num package balls */
-    const char *segment         = sku_info[match_index].segment; /* Market segment */
-    char prod_phase[4];         /* Blank = production, PR = Prototype, ES = Engineering sample */
-    char prod_rev[5];           /* Product revision */
-    const char *rohs_option     = "G"; /* RoHS is always G for current parts */
-
-    /* Update the model number with the number of cores */
-    model = (model / 100) * 100 + model_digits_for_cores(cores);
-
-    /* Update the RCLK setting based on MIO_FUS_DAT3[core_pll_mul] */
-    uint64_t core_pll_mul;
-    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
-    {
-        BDK_CSR_INIT(mio_fus_dat3, node, BDK_MIO_FUS_DAT3);
-        core_pll_mul = mio_fus_dat3.s.core_pll_mul;
-    }
-    else
-        core_pll_mul = bdk_fuse_read_range(bdk_numa_local(), BDK_FUS_FUSE_NUM_E_CORE_MAX_MULX(0), 7);
-
-    if (core_pll_mul)
-    {
-        /* CORE_PLL_MUL covers bits 5:1, so we need to multiple by 2. The
-           documentation doen't mention this clearly: There is a 300Mhz
-           addition to the base multiplier */
-        rclk_limit = core_pll_mul * 2 * 50 + 300;
-    }
-
-    /* FIXME: Hardcode production as there is no way to tell */
-    prod_phase[0] = 0;
-
-    /* Read the Pass information from fuses. Note that pass info in
-       MIO_FUS_DAT2[CHIP_ID] is encoded as
-            bit[7] = Unused, zero
-            bit[6] = Alternate package
-            bit[5..3] = Major pass
-            bit[2..0] = Minor pass */
-    int major_pass;
-    int minor_pass;
-    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
-    {
-        BDK_CSR_INIT(mio_fus_dat2, node, BDK_MIO_FUS_DAT2);
-        major_pass = ((mio_fus_dat2.s.chip_id >> 3) & 7) + 1;
-        minor_pass = mio_fus_dat2.s.chip_id & 7;
-    }
-    else
-    {
-        /* FIXME: We don't support getting the pass for other node on CN9XXX */
-        bdk_ap_midr_el1_t midr_el1;
-        BDK_MRS(MIDR_EL1, midr_el1.u);
-        major_pass = (midr_el1.s.variant & 7) + 1;
-        minor_pass = midr_el1.s.revision;
-    }
-
-    if (major_pass == 1)
-    {
-        /* Pass 1.x is special in that we don't show the implied 'X' */
-        if (minor_pass == 0)
-        {
-            /* Completely blank for 1.0 */
-            prod_rev[0] = 0;
-        }
-        else
-        {
-            /* If we are production and not pass 1.0, the product phase
-               changes from blank to "-P". The product revision then
-               follows the product phase without a '-' */
-            if (prod_phase[0] == 0)
-            {
-                /* Change product phase to "-P" */
-                prod_phase[0] = '-';
-                prod_phase[1] = 'P';
-                prod_phase[2] = 0;
-            }
-            /* No separator between phase and revision */
-            prod_rev[0] = '1';
-            prod_rev[1] = '0' + minor_pass;
-            prod_rev[2] = 0;
-        }
-    }
-    else
-    {
-        /* Pass 2.0 and above        12345678 */
-        const char pass_letter[8] = "XYWVUTSR";
-        prod_rev[0] = '-';
-        prod_rev[1] = pass_letter[major_pass-1];
-        if (minor_pass == 0)
-        {
-            /* Nothing after the letter code */
-            prod_rev[2] = 0;
-        }
-        else
-        {
-            /* Add major and minor after the letter code */
-            prod_rev[2] = '0' + major_pass;
-            prod_rev[3] = '0' + minor_pass;
-            prod_rev[4] = 0;
-        }
-    }
-
-    /* Special check for CN88XX pass 2.0 and 2.1. Documentation mistakenly
-       specified 2.0 as -PR and 2.1 as -Y. Rather than fix the docs, OPs has
-       decided to special case this SKU */
-    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) && (major_pass == 2))
-    {
-        if (minor_pass == 0)
-        {
-            prod_phase[0] = '-'; /* SKU ends with -PR-Y-G */
-            prod_phase[1] = 'P';
-            prod_phase[2] = 'R';
-            prod_phase[3] = 0;
-        }
-        else if (minor_pass == 1)
-        {
-            prod_rev[0] = '-'; /* SKU ends with -Y-G */
-            prod_rev[1] = 'Y';
-            prod_rev[2] = 0;
-        }
-    }
-
-    /* Read PNAME fuses, looking for SKU overrides */
-    // FIXME: Implement PNAME reads
-
-    /* Build the SKU string */
-    snprintf(chip_sku[node], sizeof(chip_sku[node]), "%s%d%s-%d%s%d-%s%s%s-%s",
-        prefix, model, customer_code, rclk_limit, bg_str, balls, segment,
-        prod_phase, prod_rev, rohs_option);
-
-    return chip_sku[node];
-}
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-numa.c b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-numa.c
index 33d34ba..ede3b10 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-numa.c
+++ b/src/vendorcode/cavium/bdk/libbdk-arch/bdk-numa.c
@@ -37,11 +37,18 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include <stdio.h>
+#include <libbdk-hal/bdk-atomic.h>
 
-int __bdk_numa_master_node = -1;    /* Which node is the master */
-static int __bdk_numa_exists_mask = 0;     /* Bitmask of nodes that exist */
-static bdk_spinlock_t __bdk_numa_lock;
+/*
+ * FIXME(dhendrix): can't include bdk-spinlock.h, compile complains:
+ * {standard input}:40: Error: selected processor does not support `ldadda x3,x5,[x2]'
+ */
+
+//int __bdk_numa_master_node = -1;    /* Which node is the master */
+int __bdk_numa_master_node = 0;			/* FIXME(dhendrix): assume 0 */
+//static int __bdk_numa_exists_mask = 0;     /* Bitmask of nodes that exist */
+static int __bdk_numa_exists_mask = 1;		/* FIXME(dhendrix): assume 0x01 */
+//static bdk_spinlock_t __bdk_numa_lock;
 
 /**
  * Get a bitmask of the nodes that exist
@@ -60,11 +67,8 @@
  */
 void bdk_numa_set_exists(bdk_node_t node)
 {
-    bdk_spinlock_lock(&__bdk_numa_lock);
-    __bdk_numa_exists_mask |= 1 << node;
-    if (__bdk_numa_master_node == -1)
-        __bdk_numa_master_node = node;
-    bdk_spinlock_unlock(&__bdk_numa_lock);
+	/* FIXME(dhendrix): stub. */
+	return;
 }
 
 /**
@@ -76,7 +80,8 @@
  */
 int bdk_numa_exists(bdk_node_t node)
 {
-    return __bdk_numa_exists_mask & (1 << node);
+	/* FIXME(dhendrix): stub */
+	return node == 0;
 }
 
 /**
@@ -86,6 +91,7 @@
  */
 extern int bdk_numa_is_only_one()
 {
-    return __bdk_numa_exists_mask == 1;
+	/* FIXME(dhendrix): stub */
+	return 1;
 }
 
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-gpio.c
similarity index 65%
copy from src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
copy to src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-gpio.c
index 8cac04a..330a23b 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-gpio.c
@@ -37,23 +37,34 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include "libbdk-arch/bdk-csrs-ocla.h"
+#include "libbdk-arch/bdk-csrs-gpio.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/bdk-gpio.h"
+#include "libbdk-boot/bdk-boot-gpio.h"
 
-bdk_platform_t __bdk_platform;
-
-void __bdk_platform_init()
+/**
+ * Configure GPIO on all nodes as part of booting
+ */
+void bdk_boot_gpio(void)
 {
-    BDK_CSR_INIT(c, bdk_numa_master(), BDK_OCLAX_CONST(0));
-    if (c.u == 0)
+    const int NUM_GPIO = bdk_gpio_get_num();
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
     {
-        __bdk_platform = BDK_PLATFORM_ASIM;
-    }
-    else
-    {
-        int plat2 = bdk_fuse_read(bdk_numa_master(), 197);
-        int plat1 = bdk_fuse_read(bdk_numa_master(), 196);
-        int plat0 = bdk_fuse_read(bdk_numa_master(), 195);
-        __bdk_platform = (plat2 << 2) | (plat1 << 1) | plat0;
+        if (bdk_numa_exists(n))
+        {
+            for (int gpio = 0; gpio < NUM_GPIO; gpio++)
+            {
+                int pin_sel = bdk_config_get_int(BDK_CONFIG_GPIO_PIN_SELECT, gpio, n);
+                if (pin_sel >= 0)
+                {
+                    BDK_TRACE(INIT, "Connecting N%d.GPIO%d to pin select 0x%x\n",
+                        n, gpio, pin_sel);
+                    bdk_gpio_select_pin(n, gpio, pin_sel);
+                }
+                int invert = bdk_config_get_int(BDK_CONFIG_GPIO_POLARITY, gpio, n);
+                if (invert)
+                    BDK_CSR_MODIFY(c, n, BDK_GPIO_BIT_CFGX(gpio), c.s.pin_xor = 1);
+            }
+        }
     }
 }
-
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-pcie.c
similarity index 69%
copy from src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
copy to src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-pcie.c
index 8cac04a..b03c2e0 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-pcie.c
@@ -37,23 +37,32 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include "libbdk-arch/bdk-csrs-ocla.h"
+#include <string.h>
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-boot/bdk-boot-pcie.h"
+#include "libbdk-hal/bdk-pcie.h"
 
-bdk_platform_t __bdk_platform;
-
-void __bdk_platform_init()
+/**
+ * Configure PCIe on all nodes as part of booting
+ */
+void bdk_boot_pcie(void)
 {
-    BDK_CSR_INIT(c, bdk_numa_master(), BDK_OCLAX_CONST(0));
-    if (c.u == 0)
+    /* Initialize PCIe and bring up the link */
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
     {
-        __bdk_platform = BDK_PLATFORM_ASIM;
-    }
-    else
-    {
-        int plat2 = bdk_fuse_read(bdk_numa_master(), 197);
-        int plat1 = bdk_fuse_read(bdk_numa_master(), 196);
-        int plat0 = bdk_fuse_read(bdk_numa_master(), 195);
-        __bdk_platform = (plat2 << 2) | (plat1 << 1) | plat0;
+        if (bdk_numa_exists(n))
+        {
+            for (int p = 0; p < bdk_pcie_get_num_ports(n); p++)
+            {
+                /* Only init PCIe that are attached to QLMs */
+                if (bdk_qlm_get_qlm_num(n, BDK_IF_PCIE, p, 0) != -1)
+                {
+                    BDK_TRACE(INIT, "Initializing PCIe%d on Node %d\n", p, n);
+                    bdk_pcie_rc_initialize(n, p);
+                }
+            }
+        }
     }
 }
-
diff --git a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-qlm.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-qlm.c
new file mode 100644
index 0000000..73bdca0
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-qlm.c
@@ -0,0 +1,515 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/bdk-utils.h"
+#include "libbdk-boot/bdk-boot-qlm.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/bdk-twsi.h"
+
+static void boot_init_qlm_clk(void)
+{
+    /* Setup reference clocks */
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+    {
+        if (!bdk_numa_exists(n))
+            continue;
+
+        int num_qlms = bdk_qlm_get_num(n);
+
+        BDK_TRACE(INIT, "Initializing QLM clocks on Node %d\n", n);
+        for (int qlm = 0; qlm < num_qlms; qlm++)
+        {
+            bdk_qlm_clock_t clk = bdk_config_get_int(BDK_CONFIG_QLM_CLK, n, qlm);
+            if (BDK_QLM_CLK_LAST == clk) /* no entry */
+                continue;
+
+            if (clk > BDK_QLM_CLK_LAST)
+            {
+                bdk_warn("Invalid clock source %d for QLM%d on node %d. Not configuring.\n",
+                         clk, qlm, n);
+                continue;
+            }
+
+            if (0 != bdk_qlm_set_clock(n, qlm, clk))
+            {
+                bdk_error("Error setting clock source %d for QLM%d on node %d. Ignoring.\n",
+                          clk, qlm, n);
+            }
+        }
+    }
+}
+
+/**
+ * Given a node and DLM/QLM, return the possible BGX lanes connected to it. This
+ * is needed to determine which PHY address to use for SFP/SFP+ detection.
+ *
+ * @param node   Node the DLM/QLM is on
+ * @param qlm    DLM/QLM to find the BGX for
+ * @param bgx    Output: The BGX instance number, or -1 on failure
+ * @param bgx_lane_mask
+ *               Output: Which BGX indexes may be connected to this port
+ */
+static void find_bgx(int node, int qlm, int *bgx, int *bgx_lane_mask)
+{
+    *bgx = -1;
+    *bgx_lane_mask = 0;
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        switch (qlm)
+        {
+            case 0: /* BGX0 -> QLM0 */
+            case 1: /* BGX1 -> QLM1 */
+                *bgx = qlm;
+                *bgx_lane_mask = 0xf;
+                return;
+            default:
+                BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
+                return;
+        }
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+    {
+        switch (qlm)
+        {
+            case 2: /* BGX0 -> QLM2 */
+                *bgx = 0;
+                *bgx_lane_mask = 0xf;
+                return;
+            case 3: /* BGX1 -> QLM3 */
+                *bgx = 1;
+                *bgx_lane_mask = 0xf;
+                return;
+            case 4: /* BGX3 -> DLM4 */
+                *bgx = 3;
+                *bgx_lane_mask = 0x3;
+                return;
+            case 5: /* BGX2 -> DLM5 */
+                *bgx = 2;
+                *bgx_lane_mask = 0x3;
+                return;
+            case 6: /* BGX2 -> DLM6 */
+                *bgx = 2;
+                *bgx_lane_mask = 0xc;
+                return;
+            default:
+                BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
+                return;
+        }
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+    {
+        switch (qlm)
+        {
+            case 0: /* BGX0 -> DLM0 */
+                *bgx = 0;
+                *bgx_lane_mask = 0x3;
+                return;
+            case 1: /* BGX0 -> DLM1 */
+                *bgx = 0;
+                *bgx_lane_mask = 0xc;
+                return;
+            case 2: /* BGX1 -> DLM2 */
+                *bgx = 1;
+                *bgx_lane_mask = 0x3;
+                return;
+            case 3: /* BGX1 -> DLM3 */
+                *bgx = 1;
+                *bgx_lane_mask = 0xc;
+                return;
+            default:
+                BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
+                return;
+        }
+    }
+    else
+        bdk_error("N%d.QLM%d: Unsupported chip, update %s()\n", node, qlm, __FUNCTION__);
+}
+
+/**
+ * Determine the DLM/QLM mode based on a SFP/SFP+ connected to the port. Note that
+ * the CN8XXX parts can't control mode per lane, so all SFP/SFP+ on a DLM/QLM must
+ * be the same mode. This code is sloppy about finding the BGX PHY for the DLM/QLM
+ * because not all lanes may be used.
+ *
+ * @param node   Node to determine mode for
+ * @param qlm    DLM/QLM the SFP/SFP+ is connected to
+ *
+ * @return QLM mode or -1 on failure
+ */
+static int init_sfp(int node, int qlm)
+{
+    int mode = BDK_QLM_MODE_XFI_4X1; /* Default to XFI if detection fails */
+    int bgx = -1;
+    int bgx_lane_mask = 0;
+
+    find_bgx(node, qlm, &bgx, &bgx_lane_mask);
+    if (bgx == -1)
+        return mode;
+
+    BDK_TRACE(INIT, "N%d.QLM%d: Checking for SFP/SFP+\n", node, qlm);
+
+    for (int index = 0; index < 4; index++)
+    {
+        /* Skip BGX indexes that aren't applicable */
+        if ((bgx_lane_mask & (1 << index)) == 0)
+            continue;
+        /* Lookup the PHY address for this BGX port */
+        int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index);
+        /* SFP/SFP+ are connected with TWSI, so only check ports with
+           PHYs connected with TWSI */
+        if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI)
+            continue;
+
+        /* For TWSI:
+            Bits[31:24]: Node ID, 0xff for device node
+            Bits[23:16]: TWSI internal address width in bytes (0-2)
+            Bits[15:12]: 2=TWSI
+            Bits[11:8]: TWSI bus number
+            Bits[7:0]: TWSI address */
+        int n = (phy_addr >> 24) & 0xff;
+        int twsi_ia_width = (phy_addr >> 16) & 0xff;
+        int twsi_bus = (phy_addr >> 8) & 0xf;
+        int twsi_addr = 0x50; /* From SFP spec */
+        if (n == 0xff)
+            n = node;
+
+        /* Read bytes 0-3 from eeprom. Note read is big endian, so byte 0 is
+           bits 31:24 in the result */
+        int64_t eeprom_00_03 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 4, twsi_ia_width);
+        if (eeprom_00_03 == -1)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
+            continue;
+        }
+        int64_t eeprom_04_07 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 4, 4, twsi_ia_width);
+        if (eeprom_04_07 == -1)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
+            continue;
+        }
+        int64_t eeprom_08_11 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 8, 4, twsi_ia_width);
+        if (eeprom_08_11 == -1)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
+            continue;
+        }
+        int64_t eeprom_12 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 12, 1, twsi_ia_width);
+        if (eeprom_12 == -1)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
+            continue;
+        }
+
+        /* Byte 0: Identifier, should be 0x03 for SFP/SFP+
+                    0x03 = SFP of SFP+
+                    0x0c = QSFP
+                    0x0d = QSFP+ */
+        if (bdk_extract(eeprom_00_03, 24, 8) != 0x03)
+        {
+            /* Byte 0 of eeprom should be 0x03 for SFP/SFP+ */
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ not detected\n", node, qlm, bgx, index);
+            continue;
+        }
+        /* Byte 1: Extended Identifier, should be 0x04 */
+        if (bdk_extract(eeprom_00_03, 16, 8) != 0x04)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ incorrect extended identifier\n", node, qlm, bgx, index);
+            continue;
+        }
+        /* Byte 2: Connector
+                    Value   Description of connector
+                    00h     Unknown or unspecified
+                    01h     SC
+                    02h     Fibre Channel Style 1 copper connector
+                    03h     Fibre Channel Style 2 copper connector
+                    04h     BNC/TNC
+                    05h     Fibre Channel coaxial headers
+                    06h     FiberJack
+                    07h     LC
+                    08h     MT-RJ
+                    09h     MU
+                    0Ah     SG
+                    0Bh     Optical pigtail
+                    0Ch     MPO Parallel Optic
+                    0Dh-1Fh Reserved, Unallocated
+                    20h     HSSDC II
+                    21h     Copper Pigtail
+                    22h     RJ45
+                    23h-7Fh Reserved, Unallocated
+                    80-FFh Vendor specific */
+        bool isOptical = false;
+        switch (bdk_extract(eeprom_00_03, 8, 8))
+        {
+            case 0x01: /* SC - Short channel */
+            case 0x07: /* LC - Long channel */
+            case 0x0B: /* Optical pigtail */
+                isOptical = true;
+                break;
+        }
+        BDK_TRACE(INIT, "N%d.QLM%d: SFP/SFP+ eeprom Bytes[0:3] 0x%0llx, Bytes[4:7] 0x%08llx, [8:11] 0x%08llx [12] 0x%02llx\n",
+            node, qlm, eeprom_00_03, eeprom_04_07, eeprom_08_11, eeprom_12);
+        /* Byte 3: Transceiver info first byte. See comments below */
+        /* Byte 3, bits 4-7 correspond to 10G Ethernet speeds */
+        /* 10G Ethernet Compliance Codes
+                Byte 3[7] 10G BASE-ER (Fiber - Extended Reach)
+                Byte 3[6] 10G BASE-LRM (Fiber - Long reach multi-mode)
+                Byte 3[5] 10G BASE-LR (Fiber - Long reach)
+                Byte 3[4] 10G BASE-SR (Fiber - Short reach) */
+        bool isXFI = bdk_extract(eeprom_00_03, 0, 8) != 0;
+        /* Byte 6, bits 0-7 correspond to Gigabit Ethernet speeds */
+        /* Gigabit Ethernet Compliance Codes
+                Byte 6[7] BASE-PX
+                Byte 6[6] BASE-BX10
+                Byte 6[5] 100BASE-FX
+                Byte 6[4] 100BASE-LX/LX10 (Fiber)
+                Byte 6[3] 1000BASE-T (Twisted pair)
+                Byte 6[2] 1000BASE-CX (Shielded balanced copper)
+                Byte 6[1] 1000BASE-LX (Fiber)
+                Byte 6[0] 1000BASE-SX (Fiber) */
+        bool isSGMII = bdk_extract(eeprom_04_07, 8, 8) != 0;
+        /* Byte 12 is the nominal bit rate, units of 100 MBits/sec. */
+        int bit_rate = eeprom_12 * 100;
+        if (bit_rate)
+        {
+            BDK_TRACE(INIT, "N%d.QLM%d: Nominal bit rate %d  MBits/sec\n",
+                node, qlm, bit_rate);
+            isXFI = (bit_rate >= 10000);
+            isSGMII = (bit_rate <= 2500);
+        }
+
+        if (isXFI)
+        {
+            mode = BDK_QLM_MODE_XFI_4X1;
+            if (isOptical)
+                BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Optical\n", node, qlm);
+            else
+                BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Copper\n", node, qlm);
+        }
+        else if (isSGMII)
+        {
+            mode = BDK_QLM_MODE_SGMII_4X1;
+            if (isOptical)
+            {
+                /* This should be 1000BASE-X, gigabit over fiber */
+                BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Optical\n", node, qlm);
+            }
+            else /* This should be SGMII, gigabit over copper */
+                BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Copper\n", node, qlm);
+        }
+    }
+    return mode;
+}
+
+/**
+ * Determine the DLM/QLM mode based on a QSFP/QSFP+ connected to
+ * the port. This code is sloppy about finding the BGX PHY for
+ * the DLM/QLM because not all lanes may be used.
+ *
+ * @param node   Node to determine mode for
+ * @param qlm    DLM/QLM the SFP/SFP+ is connected to
+ *
+ * @return QLM mode or -1 on failure
+ */
+static int init_qsfp(int node, int qlm)
+{
+    int mode = BDK_QLM_MODE_XLAUI_1X4; /* Default to XLAUI if detection fails */
+    int bgx = -1;
+    int bgx_lane_mask = 0;
+
+    find_bgx(node, qlm, &bgx, &bgx_lane_mask);
+    if (bgx == -1)
+        return mode;
+
+    BDK_TRACE(INIT, "N%d.QLM%d: Checking for QSFP/QSFP+\n", node, qlm);
+    int index = 0;
+
+    /* Lookup the PHY address for this BGX port */
+    int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index);
+    /* QSFP/QSFP+ are connected with TWSI, so only check ports with
+       PHYs connected with TWSI */
+    if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI)
+        return mode;
+
+    /* For TWSI:
+        Bits[31:24]: Node ID, 0xff for device node
+        Bits[23:16]: TWSI internal address width in bytes (0-2)
+        Bits[15:12]: 2=TWSI
+        Bits[11:8]: TWSI bus number
+        Bits[7:0]: TWSI address */
+    int n = (phy_addr >> 24) & 0xff;
+    int twsi_ia_width = (phy_addr >> 16) & 0xff;
+    int twsi_bus = (phy_addr >> 8) & 0xf;
+    int twsi_addr = 0x50; /* From SFP spec */
+    if (n == 0xff)
+        n = node;
+
+    /* Byte 0: Identifier, should be 0x03 for SFP/SFP+
+                0x03 = SFP of SFP+
+                0x0c = QSFP
+                0x0d = QSFP+ */
+    int64_t eeprom_00 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 1, twsi_ia_width);
+    switch (eeprom_00)
+    {
+        case 0x03:
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ contains a SFP+\n", node, qlm, bgx);
+            mode = init_sfp(node, qlm);
+            break;
+        case 0x0c:
+        case 0x0d:
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d Found a QSFP/QSFP+, assuming 40G\n", node, qlm, bgx);
+            mode = BDK_QLM_MODE_XLAUI_1X4;
+            break;
+        default:
+            BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ not detected\n", node, qlm, bgx);
+            break;
+    }
+    return mode;
+}
+
+static void boot_init_qlm_mode(void)
+{
+    /* Check if QLM autoconfig is requested */
+    int qlm_auto = bdk_config_get_int(BDK_CONFIG_QLM_AUTO_CONFIG);
+    if (qlm_auto)
+    {
+        /* Auto configuration of QLMs
+         */
+        for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+        {
+            if (bdk_numa_exists(n))
+            {
+                BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
+                bdk_qlm_auto_config(n);
+            }
+        }
+    }
+   /* 
+    * Check if QLM autoconfig from DIP switch settings is requested
+    */
+    else if (bdk_config_get_int(BDK_CONFIG_QLM_DIP_AUTO_CONFIG))
+    {
+        BDK_TRACE(INIT, "Reading DIP Switch settings for QLM Auto configuration\n");
+
+        /* Auto configuration of QLMs
+         */
+        for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+        {
+            if (bdk_numa_exists(n))
+            {
+                BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
+                if (bdk_qlm_dip_auto_config(n))
+                    bdk_error("QLM Auto configuration failed!\n");
+            }
+        }
+
+    }
+    else
+    {
+        /* Initialize the QLMs based on configuration file settings
+         */
+
+        boot_init_qlm_clk();
+
+        for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+        {
+            if (!bdk_numa_exists(n))
+                continue;
+
+            int num_qlms = bdk_qlm_get_num(n);
+
+            BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
+            for (int qlm = 0; qlm < num_qlms; qlm++)
+            {
+                const char *cfg_val;
+
+                cfg_val = bdk_config_get_str(BDK_CONFIG_QLM_MODE, n, qlm);
+                if (!cfg_val)
+                    continue;
+
+                int mode;
+                int freq;
+                /* Check for special token telling us to configure the QLM
+                   based on the SFP/SFP+/QSFP/QSFP+ plugged into the system. */
+                if ((strcmp(cfg_val, "SFP+") == 0) || (strcmp(cfg_val, "QSFP+") == 0))
+                {
+                    if (strcmp(cfg_val, "SFP+") == 0)
+                        mode = init_sfp(n, qlm);
+                    else
+                        mode = init_qsfp(n, qlm);
+
+                    if (mode == BDK_QLM_MODE_SGMII_4X1)
+                        freq = 1250;
+                    else
+                        freq = 10321;
+                }
+                else
+                {
+                    mode = bdk_qlm_cfg_string_to_mode(cfg_val);
+                    freq = bdk_config_get_int(BDK_CONFIG_QLM_FREQ, n, qlm);
+                }
+                if (-1 == mode)
+                {
+                    bdk_error("Invalid QLM mode string '%s' for QLM%d on node %d. "
+                                "Not configuring.\n", cfg_val, qlm, n);
+                    continue;
+                }
+                if (-1 == freq)
+                {
+                    bdk_error("No frequency setting for QLM%d on node %d. "
+                                "Not configuring.\n", qlm, n);
+                    continue;
+                }
+
+                bdk_qlm_set_mode(n, qlm, mode, freq, 0);
+            }
+        }
+    }
+}
+
+/**
+ * Configure QLM on all nodes as part of booting
+ */
+void bdk_boot_qlm(void)
+{
+    boot_init_qlm_mode();
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-status.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-status.c
index 83ab14c..c91f2dd 100644
--- a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-status.c
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-status.c
@@ -38,6 +38,8 @@
 ***********************license end**************************************/
 #include <bdk.h>
 #include "libbdk-arch/bdk-csrs-mio_tws.h"
+#include "libbdk-boot/bdk-boot-status.h"
+#include <libbdk-hal/bdk-config.h>
 
 /**
  * Report boot status to the BMC or whomever might care. This function
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-usb.c
similarity index 74%
rename from src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
rename to src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-usb.c
index 8cac04a..70ed44a 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot-usb.c
@@ -37,23 +37,26 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include "libbdk-arch/bdk-csrs-ocla.h"
+#include <libbdk-hal/bdk-usb.h>
+#include <libbdk-hal/bdk-config.h>
+#include <libbdk-boot/bdk-boot-usb.h>
 
-bdk_platform_t __bdk_platform;
-
-void __bdk_platform_init()
+/**
+ * Configure USB on all nodes as part of booting
+ */
+void bdk_boot_usb(void)
 {
-    BDK_CSR_INIT(c, bdk_numa_master(), BDK_OCLAX_CONST(0));
-    if (c.u == 0)
+    /* Initialize USB, ready for standard XHCI driver */
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
     {
-        __bdk_platform = BDK_PLATFORM_ASIM;
-    }
-    else
-    {
-        int plat2 = bdk_fuse_read(bdk_numa_master(), 197);
-        int plat1 = bdk_fuse_read(bdk_numa_master(), 196);
-        int plat0 = bdk_fuse_read(bdk_numa_master(), 195);
-        __bdk_platform = (plat2 << 2) | (plat1 << 1) | plat0;
+        if (bdk_numa_exists(n))
+        {
+            for (int p = 0; p < 2; p++)
+            {
+                int usb_refclock = bdk_config_get_int(BDK_CONFIG_USB_REFCLK_SRC, n,p);
+                BDK_TRACE(INIT, "Initializing USB%d on Node %d clock type %d\n", p, n, usb_refclock);
+                bdk_usb_initialize(n, p, usb_refclock);
+            }
+        }
     }
 }
-
diff --git a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot.c
new file mode 100644
index 0000000..15bcf4a
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-boot.c
@@ -0,0 +1,98 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-boot/bdk-boot-pcie.h"
+#include "libbdk-boot/bdk-boot-qlm.h"
+#include "libbdk-boot/bdk-boot-usb.h"
+#include "libbdk-hal/bdk-pcie.h"
+#include "libbdk-hal/bdk-mdio.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/bdk-ecam.h"
+#include "libbdk-hal/bdk-rng.h"
+#include "libbdk-boot/bdk-boot-gpio.h"
+#include "libbdk-arch/bdk-csrs-iobn.h"
+#include "libbdk-arch/bdk-csrs-dap.h"
+
+/**
+ * Configure hardware
+ */
+void bdk_boot(void)
+{
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+    {
+        if (bdk_numa_exists(n))
+        {
+            /* Allow CAP access from cores so we can read system registers through
+               memory mapped addresses. See bdk_sysreg_read() */
+            BDK_CSR_MODIFY(c, n, BDK_DAP_IMP_DAR, c.s.caben = 1);
+
+            /* Enable IOBN */
+            if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+            {
+                BDK_CSR_MODIFY(c, n, BDK_IOBNX_NCB0_HP(0),
+                    c.s.hp = 1);
+                if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+                    BDK_CSR_MODIFY(c, n, BDK_IOBNX_NCB0_HP(1),
+                        c.s.hp = 0);
+            }
+
+            bdk_ecam_scan_all(n);
+            bdk_mdio_init(n);
+            bdk_qlm_init(n);
+            bdk_rng_init(n);
+        }
+    }
+
+    bdk_boot_gpio();
+    bdk_boot_usb();
+    bdk_boot_qlm();
+    bdk_boot_pcie();
+
+    /* Initialize PHYs */
+    for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
+    {
+        if (bdk_numa_exists(n))
+        {
+            bdk_if_phy_setup(n);
+        }
+    }
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-watchdog.c b/src/vendorcode/cavium/bdk/libbdk-boot/bdk-watchdog.c
deleted file mode 100644
index 48f955a..0000000
--- a/src/vendorcode/cavium/bdk/libbdk-boot/bdk-watchdog.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/***********************license start***********************************
-* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
-* reserved.
-*
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*
-*   * Redistributions of source code must retain the above copyright
-*     notice, this list of conditions and the following disclaimer.
-*
-*   * Redistributions in binary form must reproduce the above
-*     copyright notice, this list of conditions and the following
-*     disclaimer in the documentation and/or other materials provided
-*     with the distribution.
-*
-*   * Neither the name of Cavium Inc. nor the names of
-*     its contributors may be used to endorse or promote products
-*     derived from this software without specific prior written
-*     permission.
-*
-* This Software, including technical data, may be subject to U.S. export
-* control laws, including the U.S. Export Administration Act and its
-* associated regulations, and may be subject to export or import
-* regulations in other countries.
-*
-* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
-* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
-* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
-* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
-* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
-* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
-* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
-* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
-* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
-* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
-***********************license end**************************************/
-#include <bdk.h>
-#include "libbdk-arch/bdk-csrs-gti.h"
-
-/**
- * Setup the watchdog to expire in timeout_ms milliseconds. When the watchdog
- * expires, the chip three things happen:
- * 1) Expire 1: interrupt that is ignored by the BDK
- * 2) Expire 2: DEL3T interrupt, which is disabled and ignored
- * 3) Expire 3: Soft reset of the chip
- *
- * Since we want a soft reset, we actually program the watchdog to expire at
- * the timeout / 3.
- *
- * @param timeout_ms Timeout in milliseconds. If this is zero, the timeout is taken from the
- *                   global configuration option BDK_BRD_CFG_WATCHDOG_TIMEOUT
- */
-void bdk_watchdog_set(unsigned int timeout_ms)
-{
-    if (timeout_ms == 0)
-        timeout_ms = bdk_config_get_int(BDK_CONFIG_WATCHDOG_TIMEOUT);
-
-    if (timeout_ms > 0)
-    {
-        uint64_t sclk = bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_SCLK);
-        uint64_t timeout_sclk = sclk * timeout_ms / 1000;
-        /* Per comment above, we want the watchdog to expire at 3x the rate specified */
-        timeout_sclk /= 3;
-        /* Watchdog counts in 1024 cycle steps */
-        uint64_t timeout_wdog = timeout_sclk >> 10;
-        /* We can only specify the upper 16 bits of a 24 bit value. Round up */
-        timeout_wdog = (timeout_wdog + 0xff) >> 8;
-        /* If the timeout overflows the hardware limit, set max */
-        if (timeout_wdog >= 0x10000)
-            timeout_wdog = 0xffff;
-
-        BDK_TRACE(INIT, "Watchdog: Set to expire %lu SCLK cycles\n", timeout_wdog << 18);
-        BDK_CSR_MODIFY(c, bdk_numa_local(), BDK_GTI_CWD_WDOGX(bdk_get_core_num()),
-            c.s.len = timeout_wdog;
-            c.s.mode = 3);
-    }
-}
-
-/**
- * Signal the watchdog that we are still running
- */
-void bdk_watchdog_poke(void)
-{
-    BDK_CSR_WRITE(bdk_numa_local(), BDK_GTI_CWD_POKEX(bdk_get_core_num()), 0);
-}
-
-/**
- * Disable the hardware watchdog
- */
-void bdk_watchdog_disable(void)
-{
-    BDK_CSR_WRITE(bdk_numa_local(), BDK_GTI_CWD_WDOGX(bdk_get_core_num()), 0);
-    BDK_TRACE(INIT, "Watchdog: Disabled\n");
-}
-
-/**
- * Return true if the watchdog is configured and running
- *
- * @return Non-zero if watchdog is running
- */
-int bdk_watchdog_is_running(void)
-{
-    BDK_CSR_INIT(wdog, bdk_numa_local(), BDK_GTI_CWD_WDOGX(bdk_get_core_num()));
-    return wdog.s.mode != 0;
-}
-
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-address.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-address.c
index 94d7d76..acefe17 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-address.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-address.c
@@ -72,9 +72,9 @@
 
     /* LMC number is probably aliased */
     if (l2c_ctl.s.disidxalias)
-	*lmc = EXTRACT(address, 7, xbits);
+        *lmc = EXTRACT(address, 7, xbits);
     else
-	*lmc = EXTRACT(address, 7, xbits) ^ EXTRACT(address, bitno, xbits) ^ EXTRACT(address, 12, xbits);
+        *lmc = EXTRACT(address, 7, xbits) ^ EXTRACT(address, bitno, xbits) ^ EXTRACT(address, 12, xbits);
 
     /* Figure out the bank field width */
     BDK_CSR_INIT(lmcx_config, *node, BDK_LMCX_CONFIG(*lmc));
@@ -176,7 +176,7 @@
     BDK_CSR_INIT(l2c_ctl, node, BDK_L2C_CTL);
     int new_lmc = lmc;
     if (!l2c_ctl.s.disidxalias)
-	new_lmc ^= EXTRACT(address, bitno, xbits) ^ EXTRACT(address, 12, xbits);
+        new_lmc ^= EXTRACT(address, bitno, xbits) ^ EXTRACT(address, 12, xbits);
     INSERT(address, new_lmc, 7, xbits);
 
     return address;
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-config.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-config.c
index 3465c5d..5c104231 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-config.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-config.c
@@ -37,7 +37,9 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include <unistd.h>
+#include <string.h>
+#include <libbdk-hal/bdk-config.h>
+#include <libbdk-hal/bdk-l2c.h>
 
 BDK_REQUIRE_DEFINE(DRAM_CONFIG);
 
@@ -73,22 +75,6 @@
 }
 
 /**
- * Do DRAM configuration tuning
- *
- * @param node   Node to tune
- *
- * @return Success or Fail
- */
-int bdk_dram_tune(int node)
-{
-    int ret;
-    BDK_TRACE(DRAM, "N%d: Starting DRAM tuning\n", node);
-    ret = libdram_tune(node);
-    BDK_TRACE(DRAM, "N%d: DRAM tuning returned %d\n", node, ret);
-    return ret;
-}
-
-/**
  * Do all the DRAM Margin tests 
  *
  * @param node   Node to test
@@ -144,7 +130,9 @@
      * the address to make it a physical offset. Doing this simplifies the
      * address checks and calculations which only work with physical offsets.
      */
-    uint64_t top_of_bdk = (bdk_ptr_to_phys(sbrk(0)) & bdk_build_mask(40));
+	/* FIXME(dhendrix): we only care about node 0 */
+//    uint64_t top_of_bdk = (bdk_ptr_to_phys(sbrk(0)) & bdk_build_mask(40));
+    uint64_t top_of_bdk = 0;
     uint64_t l2_size = bdk_l2c_get_cache_size_bytes(bdk_numa_master());
     if (top_of_bdk <= l2_size)
     {
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-size.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-size.c
index 122afb2..8cd4594 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-size.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-size.c
@@ -37,6 +37,8 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
+#include <libbdk-hal/bdk-utils.h>
+
 
 /**
  * Return the number of LMC controllers in use
@@ -92,7 +94,7 @@
  *
  * @param node   Node to probe
  *
- */ 
+ */
 uint32_t __bdk_dram_get_row_mask(bdk_node_t node, int lmc)
 {
     // PROTECT!!!
@@ -108,7 +110,7 @@
  *
  * @param node   Node to probe
  *
- */ 
+ */
 uint32_t __bdk_dram_get_col_mask(bdk_node_t node, int lmc)
 {
     // PROTECT!!!
@@ -124,7 +126,7 @@
  *
  * @param node   Node to probe
  *
- */ 
+ */
 // all DDR3, and DDR4 x16 today, use only 3 bank bits; DDR4 x4 and x8 always have 4 bank bits
 // NOTE: this will change in the future, when DDR4 x16 devices can come with 16 banks!! FIXME!!
 int __bdk_dram_get_num_bank_bits(bdk_node_t node, int lmc)
@@ -181,9 +183,6 @@
  */
 uint64_t bdk_dram_get_size_mbytes(int node)
 {
-    if (bdk_is_platform(BDK_PLATFORM_EMULATOR))
-        return 2 << 10; /* 2GB is available on t88 and t81 
-                        ** some t83 models have 8gb, but it is too long to init */
     /* Return zero if dram isn't enabled */
     if (!__bdk_is_dram_enabled(node))
         return 0;
@@ -192,21 +191,13 @@
     const int num_dram_controllers = __bdk_dram_get_num_lmc(node);
     for (int lmc = 0; lmc < num_dram_controllers; lmc++)
     {
-        if (bdk_is_platform(BDK_PLATFORM_ASIM))
-        {
-            /* Asim doesn't simulate the rank detection, fake 4GB per controller */
-            memsize += 4ull << 30;
-        }
-        else
-        {
-            // PROTECT!!!
-	    if (__bdk_dram_is_lmc_in_dreset(node, lmc)) // check LMCn
-		return 0;
-            BDK_CSR_INIT(lmcx_config, node, BDK_LMCX_CONFIG(lmc));
-            int num_ranks = bdk_pop(lmcx_config.s.init_status);
-            uint64_t rank_size = 1ull << (28 + lmcx_config.s.pbank_lsb - lmcx_config.s.rank_ena);
-            memsize += rank_size * num_ranks;
-        }
+         // PROTECT!!!
+        if (__bdk_dram_is_lmc_in_dreset(node, lmc)) // check LMCn
+            return 0;
+        BDK_CSR_INIT(lmcx_config, node, BDK_LMCX_CONFIG(lmc));
+        int num_ranks = bdk_pop(lmcx_config.s.init_status);
+        uint64_t rank_size = 1ull << (28 + lmcx_config.s.pbank_lsb - lmcx_config.s.rank_ena);
+        memsize += rank_size * num_ranks;
     }
     return memsize >> 20;
 }
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-addrbus.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-addrbus.c
index 9fe8570..834ade4 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-addrbus.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-addrbus.c
@@ -37,6 +37,7 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include "bdk.h"
+#include <libbdk-hal/bdk-utils.h>
 
 /* Used for all memory reads/writes related to the test */
 #define READ64(address) __bdk_dram_read64(address)
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-databus.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-databus.c
index c3fa1ff..b7a6f96 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-databus.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-databus.c
@@ -38,6 +38,8 @@
 ***********************license end**************************************/
 #include "bdk.h"
 
+#include <libbdk-hal/bdk-utils.h>
+
 /* Used for all memory reads/writes related to the test */
 #define READ64(address) __bdk_dram_read64(address)
 #define WRITE64(address, data) __bdk_dram_write64(address, data)
@@ -97,7 +99,7 @@
  */
 static int write_data_bus_burst(uint64_t address, int bursts)
 {
-    BDK_TRACE(DRAM_TEST, "[0x%016lx:0x%016lx] Writing incrementing digits\n",
+    BDK_TRACE(DRAM_TEST, "[0x%016llx:0x%016llx] Writing incrementing digits\n",
         address, address + 127);
     /* Loop over the burst so people using a scope have time to capture
        traces */
@@ -164,7 +166,7 @@
  */
 static void write_data_bus_walk(uint64_t address, int burst, uint64_t pattern)
 {
-    BDK_TRACE(DRAM_TEST, "[0x%016lx:0x%016lx] Writing walking pattern 0x%016lx\n",
+    BDK_TRACE(DRAM_TEST, "[0x%016llx:0x%016llx] Writing walking pattern 0x%016llx\n",
         address, address + 127, pattern);
 
     uint64_t a = address;
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-fastscan.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-fastscan.c
index 46e205d..c89ef76 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-fastscan.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-fastscan.c
@@ -37,6 +37,7 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include "bdk.h"
+#include <libbdk-hal/bdk-utils.h>
 
 /* Used for all memory reads/writes related to the test */
 #define READ64(address) __bdk_dram_read64(address)
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-patfil.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-patfil.c
index e6c4b57..6315172 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-patfil.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test-patfil.c
@@ -37,6 +37,8 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include "bdk.h"
+#include <libbdk-hal/bdk-rng.h>
+#include <libbdk-hal/bdk-utils.h>
 
 // choose prediction-based algorithms for mem_xor and mem_rows tests
 #define USE_PREDICTION_CODE_VERSIONS 1   // change to 0 to go back to the original versions
@@ -286,7 +288,7 @@
     int failures = 0;
 
     /* Pass 1 ascending addresses, fill memory with pattern. */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase1, address incrementing, pattern 0x%016lx\n", area, max_address-1, pattern);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase1, address incrementing, pattern 0x%016llx\n", area, max_address-1, pattern);
     for (uint64_t address = area; address < max_address; address += 8)
         WRITE64(address, pattern);
 
@@ -294,7 +296,7 @@
     BDK_DCACHE_INVALIDATE;
 
     /* Pass 2: ascending addresses, read pattern and write ~pattern */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase2, address incrementing, pattern 0x%016lx\n", area, max_address-1, ~pattern);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase2, address incrementing, pattern 0x%016llx\n", area, max_address-1, ~pattern);
     for (uint64_t address = area; address < max_address; address += 8)
     {
         uint64_t data = READ64(address);
@@ -307,7 +309,7 @@
     BDK_DCACHE_INVALIDATE;
 
     /* Pass 3: ascending addresses, read ~pattern and write pattern. */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase3, address incrementing, pattern 0x%016lx\n", area, max_address-1, pattern);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase3, address incrementing, pattern 0x%016llx\n", area, max_address-1, pattern);
     for (uint64_t address = area; address < max_address; address += 8)
     {
         uint64_t data = READ64(address);
@@ -320,7 +322,7 @@
     BDK_DCACHE_INVALIDATE;
 
     /* Pass 4: descending addresses, read pattern and write ~pattern. */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase4, address decrementing, pattern 0x%016lx\n", area, max_address-1, ~pattern);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase4, address decrementing, pattern 0x%016llx\n", area, max_address-1, ~pattern);
     uint64_t end = max_address - sizeof(uint64_t);
     for (uint64_t address = end; address >= area; address -= 8)
     {
@@ -334,7 +336,7 @@
     BDK_DCACHE_INVALIDATE;
 
     /* Pass 5: descending addresses, read ~pattern and write pattern. */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase5, address decrementing, pattern 0x%016lx\n", area, max_address-1, pattern);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase5, address decrementing, pattern 0x%016llx\n", area, max_address-1, pattern);
     for (uint64_t address = end; address >= area; address -= 8)
     {
         uint64_t data = READ64(address);
@@ -347,7 +349,7 @@
     BDK_DCACHE_INVALIDATE;
 
     /* Pass 6: ascending addresses, read pattern. */
-    BDK_TRACE(DRAM_TEST, "    [0x%016lx:0x%016lx] Phase6, address incrementing\n", area, max_address-1);
+    BDK_TRACE(DRAM_TEST, "    [0x%016llx:0x%016llx] Phase6, address incrementing\n", area, max_address-1);
     for (uint64_t address = area; address < max_address; address += 8)
     {
         uint64_t data = READ64(address);
@@ -660,7 +662,7 @@
         WRITE64(address1         , p);
         WRITE64(address1 + offset, p);
         address1 += 8;
-	p += pincr;
+        p += pincr;
     }
     __bdk_dram_flush_to_mem_range(area, max_address);
     BDK_DCACHE_INVALIDATE;
@@ -674,7 +676,7 @@
         address1 = area;
 
         this_pattern = bdk_rng_get_random64();
-	pattern2 ^= this_pattern;
+        pattern2 ^= this_pattern;
 
         while (address1 < area2)
         {
@@ -693,13 +695,13 @@
         BDK_DCACHE_INVALIDATE;
 
         /* Look for differences from the expected pattern in both areas.
-	 * If there is a mismatch, reset the appropriate memory location
-	 * with the correct pattern. Failing to do so
+         * If there is a mismatch, reset the appropriate memory location
+         * with the correct pattern. Failing to do so
          * means that on all subsequent passes the erroring locations
-	 * will be out of sync, giving spurious errors.
+         * will be out of sync, giving spurious errors.
          */
         address1 = area;
-	ppred = pbase;
+        ppred = pbase;
 
         while (address1 < area2)
         {
@@ -712,21 +714,21 @@
             d1 = READ64(address1         );
             d2 = READ64(address1 + offset);
 
-	    p = ppred ^ pattern2;
+            p = ppred ^ pattern2;
 
             if (bdk_unlikely(d1 != p)) {
-		failures += __bdk_dram_retry_failure(burst, address1, d1, p);
+                failures += __bdk_dram_retry_failure(burst, address1, d1, p);
                 // Synchronize the area, adjusting for the error.
                 //WRITE64(address1, p); // retries should do this
             }
             if (bdk_unlikely(d2 != p)) {
-		failures += __bdk_dram_retry_failure(burst, address1 + offset, d2, p);
+                failures += __bdk_dram_retry_failure(burst, address1 + offset, d2, p);
                 // Synchronize the area, adjusting for the error.
                 //WRITE64(address1 + offset, p); // retries should do this
             }
 
             address1 += 8;
-	    ppred += pincr;
+            ppred += pincr;
 
         } /* while (address1 < area2) */
     } /* for (int burst = 0; burst < bursts; burst++) */
@@ -761,7 +763,7 @@
         WRITE64(address1         , pattern2);
         WRITE64(address1 + offset, pattern2);
         address1 += 8;
-	pattern2 = ~pattern2; // flip for next slots
+        pattern2 = ~pattern2; // flip for next slots
     }
 
     __bdk_dram_flush_to_mem_range(area, max_address);
@@ -771,7 +773,7 @@
     for (burst = 0; burst < bursts; burst++)
     {
         /* Invert the data, applying the change to both memory areas. Thus on
-	 * alternate passes, the data flips from 0 to 1 and vice versa.
+         * alternate passes, the data flips from 0 to 1 and vice versa.
          */
         address1 = area;
 
@@ -796,8 +798,8 @@
          * out of sync giving spurious errors.
          */
         address1 = area;
-	pattern1 = ~pattern1; // flip the starting pattern to match above loop
-	pattern2 = pattern1;  // slots have been flipped by the above loop
+        pattern1 = ~pattern1; // flip the starting pattern to match above loop
+        pattern2 = pattern1;  // slots have been flipped by the above loop
 
         while (address1 < area2)
         {
@@ -810,18 +812,18 @@
             d2 = READ64(address1 + offset);
 
             if (bdk_unlikely(d1 != pattern2)) {
-		failures += __bdk_dram_retry_failure(burst, address1, d1, pattern2);
+                failures += __bdk_dram_retry_failure(burst, address1, d1, pattern2);
                 // Synchronize the area, adjusting for the error.
                 //WRITE64(address1, pattern2); // retries should do this
             }
             if (bdk_unlikely(d2 != pattern2)) {
-		failures += __bdk_dram_retry_failure(burst, address1 + offset, d2, pattern2);
+                failures += __bdk_dram_retry_failure(burst, address1 + offset, d2, pattern2);
                 // Synchronize the two areas, adjusting for the error.
                 //WRITE64(address1 + offset, pattern2); // retries should do this
             }
 
             address1 += 8;
-	    pattern2 = ~pattern2; // flip for next pair of slots
+            pattern2 = ~pattern2; // flip for next pair of slots
         }
     }
     return failures;
diff --git a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test.c b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test.c
index 5313750..4f54b69 100644
--- a/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test.c
+++ b/src/vendorcode/cavium/bdk/libbdk-dram/bdk-dram-test.c
@@ -40,6 +40,14 @@
 #include "libbdk-arch/bdk-csrs-gti.h"
 #include "libbdk-arch/bdk-csrs-ocx.h"
 
+#include <bdk-minimal.h>        /* for printf --> printk */
+#include <libbdk-dram/bdk-dram-test.h>
+#include <libbdk-hal/bdk-atomic.h>
+#include <libbdk-hal/bdk-clock.h>
+#include <libbdk-hal/bdk-utils.h>
+#include <libbdk-os/bdk-init.h>
+#include <libbdk-os/bdk-thread.h>
+
 /* This code is an optional part of the BDK. It is only linked in
     if BDK_REQUIRE() needs it */
 BDK_REQUIRE_DEFINE(DRAM_TEST);
@@ -170,7 +178,7 @@
     start_address = bdk_numa_get_address(test_node, start_address);
     end_address = bdk_numa_get_address(test_node, end_address);
     /* Test the region */
-    BDK_TRACE(DRAM_TEST, "  Node %d, core %d, Testing [0x%011lx:0x%011lx]\n",
+    BDK_TRACE(DRAM_TEST, "  Node %d, core %d, Testing [0x%011llx:0x%011llx]\n",
         bdk_numa_local(), bdk_get_core_num() & 127, start_address, end_address - 1);
     test_info->test_func(start_address, end_address, bursts);
 
@@ -197,7 +205,7 @@
 {
     /* Figure out the addess of the byte one off the top of memory */
     uint64_t max_address = bdk_dram_get_size_mbytes(bdk_numa_local());
-    BDK_TRACE(DRAM_TEST, "DRAM available per node: %lu MB\n", max_address);
+    BDK_TRACE(DRAM_TEST, "DRAM available per node: %llu MB\n", max_address);
     max_address <<= 20;
 
     /* Make sure we have enough */
@@ -218,13 +226,13 @@
         if (max_address > (1ull << 43)) /* 43 bits in CN9XXX */
             max_address = 1ull << 43;
     }
-    BDK_TRACE(DRAM_TEST, "DRAM max address: 0x%011lx\n", max_address-1);
+    BDK_TRACE(DRAM_TEST, "DRAM max address: 0x%011llx\n", max_address-1);
 
     /* Make sure the start address is lower than the top of memory */
     if (start_address >= max_address)
     {
-        bdk_error("Start address is larger than the amount of memory: 0x%011lx versus 0x%011lx\n",
-	          start_address, max_address);
+        bdk_error("Start address is larger than the amount of memory: 0x%011llx versus 0x%011llx\n",
+                  start_address, max_address);
         return -1;
     }
     if (length == (uint64_t)-1)
@@ -260,8 +268,8 @@
             }
     }
     if (!(flags & BDK_DRAM_TEST_NO_BANNERS))
-        printf("Starting Test \"%s\" for [0x%011lx:0x%011lx] using %d core(s)\n",
-	   test_info->name, start_address, end_address - 1, total_cores_all_nodes);
+        printf("Starting Test \"%s\" for [0x%011llx:0x%011llx] using %d core(s)\n",
+           test_info->name, start_address, end_address - 1, total_cores_all_nodes);
 
     /* Remember the LMC perf counters for stats after the test */
     uint64_t start_dram_dclk[BDK_NUMA_MAX_NODES][4];
@@ -332,15 +340,15 @@
         /* Poke the watchdog */
         BDK_CSR_WRITE(bdk_numa_local(), BDK_GTI_CWD_POKEX(0), 0);
 
-	/* disable progress output when batch mode is ON  */
+        /* disable progress output when batch mode is ON  */
         if (!(flags & BDK_DRAM_TEST_NO_PROGRESS)) {
 
             /* Report progress percentage */
             int percent_x10 = (work_address - start_address) * 1000 / (end_address - start_address);
-            printf("  %3d.%d%% complete, testing [0x%011lx:0x%011lx]\r",
+            printf("  %3d.%d%% complete, testing [0x%011llx:0x%011llx]\r",
                    percent_x10 / 10, percent_x10 % 10,  work_address, work_address + size - 1);
             fflush(stdout);
-	}
+        }
 
         work_address += size;
 
@@ -357,17 +365,8 @@
                 {
                     if (per_node >= max_cores)
                         break;
-                    int run_node = (flags & BDK_DRAM_TEST_USE_CCPI) ? node ^ 1 : node;
                     BDK_TRACE(DRAM_TEST, "Starting thread %d on node %d for memory test\n", per_node, node);
-                    if (bdk_thread_create(run_node, 0, dram_test_thread, per_node, (void *)test_info, 0))
-                    {
-                        bdk_error("Failed to create thread %d for memory test on node %d\n", per_node, node);
-                    }
-                    else
-                    {
-                        per_node++;
-                        total_count++;
-                    }
+                    dram_test_thread(per_node, (void *)test_info);
                 }
             }
         }
@@ -384,7 +383,6 @@
         uint64_t period = bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME) * TIMEOUT_SECS; // FIXME? 
         uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + period;
         do {
-            bdk_thread_yield();
             cur_count = bdk_atomic_get64(&dram_test_thread_done);
             cur_time = bdk_clock_get_count(BDK_CLOCK_TIME);
             if (cur_time >= timeout) {
@@ -430,7 +428,7 @@
     if (!(flags & BDK_DRAM_TEST_NO_PROGRESS)) {
 
         /* Report progress percentage as complete */
-        printf("  %3d.%d%% complete, testing [0x%011lx:0x%011lx]\n",
+        printf("  %3d.%d%% complete, testing [0x%011llx:0x%011llx]\n",
                100, 0,  start_address, end_address - 1);
         fflush(stdout);
     }
@@ -450,7 +448,7 @@
                     if (dclk == 0)
                         dclk = 1;
                     uint64_t percent_x10 = ops * 1000 / dclk;
-                    printf("  Node %d, LMC%d: ops %lu, cycles %lu, used %lu.%lu%%\n",
+                    printf("  Node %d, LMC%d: ops %llu, cycles %llu, used %llu.%llu%%\n",
                         node, i, ops, dclk, percent_x10 / 10, percent_x10 % 10);
                 }
             }
@@ -471,7 +469,7 @@
                         if (total == 0)
                             continue;
                         uint64_t percent_x10 = busy * 1000 / total;
-                        printf("  Node %d, CCPI%d: busy %lu, total %lu, used %lu.%lu%%\n",
+                        printf("  Node %d, CCPI%d: busy %llu, total %llu, used %llu.%llu%%\n",
                             node, link, busy, total, percent_x10 / 10, percent_x10 % 10);
                     }
                 }
@@ -543,11 +541,13 @@
 
     /* Clear ECC error counters before starting the test */
     for (int chan = 0; chan < BDK_MAX_MEM_CHANS; chan++) {
-	bdk_atomic_set64(&__bdk_dram_ecc_single_bit_errors[chan], 0);
-	bdk_atomic_set64(&__bdk_dram_ecc_double_bit_errors[chan], 0);
+        bdk_atomic_set64(&__bdk_dram_ecc_single_bit_errors[chan], 0);
+        bdk_atomic_set64(&__bdk_dram_ecc_double_bit_errors[chan], 0);
     }
 
     /* Make sure at least one core from each node is running */
+    /* FIXME(dhendrix): we only care about core0 on node0 for now */
+#if 0
     for (bdk_node_t node = BDK_NODE_0; node < BDK_NUMA_MAX_NODES; node++)
     {
         if (flags & (1<<node))
@@ -557,17 +557,11 @@
                 bdk_init_cores(use_node, 1);
         }
     }
+#endif
 
     /* This returns any data compare errors found */
     int errors = __bdk_dram_run_test(&TEST_INFO[test], start_address, length, flags);
 
-    /* Poll for any errors right now to make sure any ECC errors are reported */
-    for (bdk_node_t node = BDK_NODE_0; node < BDK_NUMA_MAX_NODES; node++)
-    {
-        if (bdk_numa_exists(node) && bdk_error_check)
-            bdk_error_check(node);
-    }
-
     /* Check ECC error counters after the test */
     int64_t ecc_single = 0;
     int64_t ecc_double = 0;
@@ -582,14 +576,14 @@
     /* Always print any ECC errors */
     if (ecc_single || ecc_double)
     {
-        printf("Test \"%s\": ECC errors, %ld/%ld/%ld/%ld corrected, %ld/%ld/%ld/%ld uncorrected\n",
-	       name,
-	       ecc_single_errs[0], ecc_single_errs[1], ecc_single_errs[2], ecc_single_errs[3],
-	       ecc_double_errs[0], ecc_double_errs[1], ecc_double_errs[2], ecc_double_errs[3]);
+        printf("Test \"%s\": ECC errors, %lld/%lld/%lld/%lld corrected, %lld/%lld/%lld/%lld uncorrected\n",
+               name,
+               ecc_single_errs[0], ecc_single_errs[1], ecc_single_errs[2], ecc_single_errs[3],
+               ecc_double_errs[0], ecc_double_errs[1], ecc_double_errs[2], ecc_double_errs[3]);
     }
     if (errors || ecc_double || ecc_single) {
-	printf("Test \"%s\": FAIL: %ld single, %ld double, %d compare errors\n",
-	       name, ecc_single, ecc_double, errors);
+        printf("Test \"%s\": FAIL: %lld single, %lld double, %d compare errors\n",
+               name, ecc_single, ecc_double, errors);
     }
     else
         BDK_TRACE(DRAM_TEST, "Test \"%s\": PASS\n", name);
@@ -610,7 +604,7 @@
     bdk_dram_address_extract_info(address, &node, &lmc, &dimm, &prank, &lrank, &bank, &row, &col);
 
     snprintf(buffer, len, "[0x%011lx] (N%d,LMC%d,DIMM%d,Rank%d/%d,Bank%02d,Row 0x%05x,Col 0x%04x)",
-	     address, node, lmc, dimm, prank, lrank, bank, row, col);
+             address, node, lmc, dimm, prank, lrank, bank, row, col);
 }
 
 /**
@@ -632,22 +626,22 @@
     for (int i = 0; i < 8; i++) {
         bits = xor & 0xffULL;
         xor >>= 8;
-	if (bits) {
-	    if (byte != 8) {
-		byte = 9; // means more than 1 byte-lane was present
+        if (bits) {
+            if (byte != 8) {
+                byte = 9; // means more than 1 byte-lane was present
                 print_bits = orig_xor; // print the full original
-		break; // quit now
-	    } else {
-		byte = i; // keep checking
+                break; // quit now
+            } else {
+                byte = i; // keep checking
                 print_bits = bits;
-	    }
-	}
+            }
+        }
     }
-	
+
     bdk_dram_address_extract_info(address, &node, &lmc, &dimm, &prank, &lrank, &bank, &row, &col);
 
     snprintf(buffer, len, "N%d.LMC%d: CMP byte %d xor 0x%02lx (DIMM%d,Rank%d/%d,Bank%02d,Row 0x%05x,Col 0x%04x)[0x%011lx]",
-	     node, lmc, byte, print_bits, dimm, prank, lrank, bank, row, col, address);
+             node, lmc, byte, print_bits, dimm, prank, lrank, bank, row, col, address);
 }
 
 /**
@@ -671,15 +665,15 @@
 
     if (errors < MAX_ERRORS_TO_REPORT)
     {
-	if (fails < 0) {
-	    snprintf(failbuf, sizeof(failbuf), " ");
-	} else {
+        if (fails < 0) {
+            snprintf(failbuf, sizeof(failbuf), " ");
+        } else {
             int percent_x10 = fails * 1000 / RETRY_LIMIT;
-	    snprintf(failbuf, sizeof(failbuf), ", retries failed %3d.%d%%",
+            snprintf(failbuf, sizeof(failbuf), ", retries failed %3d.%d%%",
                      percent_x10 / 10, percent_x10 % 10);
-	}
+        }
 
-	__bdk_dram_report_address_decode_new(address, xor, buffer, sizeof(buffer));
+        __bdk_dram_report_address_decode_new(address, xor, buffer, sizeof(buffer));
         bdk_error("%s%s\n", buffer, failbuf);
 
         if (errors == MAX_ERRORS_TO_REPORT-1)
@@ -702,26 +696,26 @@
  * @return Zero if a message was logged, non-zero if the error limit has been reached
  */
 void __bdk_dram_report_error2(uint64_t address1, uint64_t data1, uint64_t address2, uint64_t data2,
-			      int burst, int fails)
+                              int burst, int fails)
 {
     int64_t errors = bdk_atomic_fetch_and_add64(&dram_test_thread_errors, 1);
     if (errors < MAX_ERRORS_TO_REPORT)
     {
-	char buffer1[80], buffer2[80];
-	char failbuf[32];
+        char buffer1[80], buffer2[80];
+        char failbuf[32];
 
-	if (fails < 0) {
-	    snprintf(failbuf, sizeof(failbuf), " ");
-	} else {
-	    snprintf(failbuf, sizeof(failbuf), ", retried %d failed %d", RETRY_LIMIT, fails);
-	}
-	__bdk_dram_report_address_decode(address1, buffer1, sizeof(buffer1));
-	__bdk_dram_report_address_decode(address2, buffer2, sizeof(buffer2));
+        if (fails < 0) {
+            snprintf(failbuf, sizeof(failbuf), " ");
+        } else {
+            snprintf(failbuf, sizeof(failbuf), ", retried %d failed %d", RETRY_LIMIT, fails);
+        }
+        __bdk_dram_report_address_decode(address1, buffer1, sizeof(buffer1));
+        __bdk_dram_report_address_decode(address2, buffer2, sizeof(buffer2));
 
-        bdk_error("compare: data1: 0x%016lx, xor: 0x%016lx%s\n"
-		  "       %s\n       %s\n",
-		  data1, data1 ^ data2, failbuf,
-		  buffer1, buffer2);
+        bdk_error("compare: data1: 0x%016llx, xor: 0x%016llx%s\n"
+                  "       %s\n       %s\n",
+                  data1, data1 ^ data2, failbuf,
+                  buffer1, buffer2);
 
         if (errors == MAX_ERRORS_TO_REPORT-1)
             bdk_error("No further DRAM errors will be reported\n");
@@ -741,23 +735,23 @@
     // bypass the retries if we are already over the limit...
     if (bdk_atomic_get64(&dram_test_thread_errors) < MAX_ERRORS_TO_REPORT) {
 
-	/* Try re-reading the memory location. A transient error may fail
-	 * on one read and work on another. Keep on retrying even when a
-	 * read succeeds.
-	 */
-	for (int i = 0; i < RETRY_LIMIT; i++) {
+        /* Try re-reading the memory location. A transient error may fail
+         * on one read and work on another. Keep on retrying even when a
+         * read succeeds.
+         */
+        for (int i = 0; i < RETRY_LIMIT; i++) {
 
-	    __bdk_dram_flush_to_mem(address);
-	    BDK_DCACHE_INVALIDATE;
+            __bdk_dram_flush_to_mem(address);
+            BDK_DCACHE_INVALIDATE;
 
-	    uint64_t new = __bdk_dram_read64(address);
+            uint64_t new = __bdk_dram_read64(address);
 
-	    if (new != expected) {
-		refail++;
-	    }
-	}
+            if (new != expected) {
+                refail++;
+            }
+        }
     } else
-	refail = -1;
+        refail = -1;
 
     // this will increment the errors always, but maybe not print...
     __bdk_dram_report_error(address, data, expected, burst, refail);
@@ -779,20 +773,20 @@
     // bypass the retries if we are already over the limit...
     if (bdk_atomic_get64(&dram_test_thread_errors) < MAX_ERRORS_TO_REPORT) {
 
-	for (int i = 0; i < RETRY_LIMIT; i++) {
-	    __bdk_dram_flush_to_mem(address1);
-	    __bdk_dram_flush_to_mem(address2);
-	    BDK_DCACHE_INVALIDATE;
+        for (int i = 0; i < RETRY_LIMIT; i++) {
+            __bdk_dram_flush_to_mem(address1);
+            __bdk_dram_flush_to_mem(address2);
+            BDK_DCACHE_INVALIDATE;
 
-	    uint64_t d1 = __bdk_dram_read64(address1);
-	    uint64_t d2 = __bdk_dram_read64(address2);
+            uint64_t d1 = __bdk_dram_read64(address1);
+            uint64_t d2 = __bdk_dram_read64(address2);
 
-	    if (d1 != d2) {
-		refail++;
-	    }
-	}
+            if (d1 != d2) {
+                refail++;
+            }
+        }
     } else
-	refail = -1;
+        refail = -1;
 
     // this will increment the errors always, but maybe not print...
     __bdk_dram_report_error2(address1, data1, address2, data2, burst, refail);
@@ -854,7 +848,7 @@
     BDK_CSR_WRITE(node, BDK_LMCX_CHAR_MASK2(lmc), 0);
 
     /* Read back the data, which should now cause an error */
-    printf("Loading the injected error address 0x%lx, node=%d, lmc=%d, dimm=%d, rank=%d/%d, bank=%d, row=%d, col=%d\n",
+    printf("Loading the injected error address 0x%llx, node=%d, lmc=%d, dimm=%d, rank=%d/%d, bank=%d, row=%d, col=%d\n",
            address, node, lmc, dimm, prank, lrank, bank, row, col);
     __bdk_dram_read64(aligned_address);
 }
diff --git a/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-mdio.c b/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-mdio.c
new file mode 100644
index 0000000..7f13d7c
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-mdio.c
@@ -0,0 +1,351 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-arch/bdk-csrs-pccpf.h>
+#include <libbdk-arch/bdk-csrs-smi.h>
+#include <libbdk-hal/device/bdk-device.h>
+#include <libbdk-hal/bdk-mdio.h>
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(MDIO);
+
+/* To maintain backwards compatibility for the old MDIO API we need
+   to lookup the MDIO device on the ECAM bus by ID. This defines
+   the ID */
+#define MDIO_DEVID ((BDK_PCC_PROD_E_GEN << 24) | BDK_PCC_VENDOR_E_CAVIUM | (BDK_PCC_DEV_IDL_E_SMI << 16))
+
+#define BDK_MDIO_TIMEOUT   100000 /* 100 millisec */
+
+/* Operating request encodings. */
+#define MDIO_CLAUSE_22_WRITE    0
+#define MDIO_CLAUSE_22_READ     1
+
+#define MDIO_CLAUSE_45_ADDRESS  0
+#define MDIO_CLAUSE_45_WRITE    1
+#define MDIO_CLAUSE_45_READ_INC 2
+#define MDIO_CLAUSE_45_READ     3
+
+/**
+ * Helper function to put MDIO interface into clause 45 mode
+ *
+ * @param bus_id
+ */
+static void __bdk_mdio_set_clause45_mode(const bdk_device_t *device, int bus_id)
+{
+    bdk_smi_x_clk_t smi_clk;
+    /* Put bus into clause 45 mode */
+    smi_clk.u = BDK_BAR_READ(device, BDK_SMI_X_CLK(bus_id));
+    if (smi_clk.s.mode != 1)
+    {
+        smi_clk.s.mode = 1;
+        smi_clk.s.preamble = 1;
+        BDK_BAR_WRITE(device, BDK_SMI_X_CLK(bus_id), smi_clk.u);
+    }
+}
+
+/**
+ * Helper function to put MDIO interface into clause 22 mode
+ *
+ * @param bus_id
+ */
+static void __bdk_mdio_set_clause22_mode(const bdk_device_t *device, int bus_id)
+{
+    bdk_smi_x_clk_t smi_clk;
+    /* Put bus into clause 22 mode */
+    smi_clk.u = BDK_BAR_READ(device, BDK_SMI_X_CLK(bus_id));
+    if (smi_clk.s.mode != 0)
+    {
+        smi_clk.s.mode = 0;
+        BDK_BAR_WRITE(device, BDK_SMI_X_CLK(bus_id), smi_clk.u);
+    }
+}
+
+/**
+ * @INTERNAL
+ * Function to read SMIX_RD_DAT and check for timeouts. This
+ * code sequence is done fairly often, so put in in one spot.
+ *
+ * @param bus_id SMI/MDIO bus to read
+ *
+ * @return Value of SMIX_RD_DAT. pending will be set on
+ *         a timeout.
+ */
+static bdk_smi_x_rd_dat_t __bdk_mdio_read_rd_dat(const bdk_device_t *device, int bus_id)
+{
+    bdk_smi_x_rd_dat_t smi_rd;
+    uint64_t done = bdk_clock_get_count(BDK_CLOCK_TIME) + (uint64_t)BDK_MDIO_TIMEOUT *
+                       bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME) / 1000000;
+    do
+    {
+        smi_rd.u = BDK_BAR_READ(device, BDK_SMI_X_RD_DAT(bus_id));
+    } while (smi_rd.s.pending && (bdk_clock_get_count(BDK_CLOCK_TIME) < done));
+    return smi_rd;
+}
+
+
+/**
+ * Perform an MII read. This function is used to read PHY
+ * registers controlling auto negotiation.
+ *
+ * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
+ *                 support multiple busses.
+ * @param phy_id   The MII phy id
+ * @param location Register location to read
+ *
+ * @return Result from the read or -1 on failure
+ */
+int bdk_mdio_read(bdk_node_t node, int bus_id, int phy_id, int location)
+{
+    const bdk_device_t *device = bdk_device_lookup(node, MDIO_DEVID, 0);
+    if (!device)
+    {
+        bdk_error("MDIO: ECAM device not found\n");
+        return -1;
+    }
+    bdk_smi_x_cmd_t smi_cmd;
+    bdk_smi_x_rd_dat_t smi_rd;
+
+    __bdk_mdio_set_clause22_mode(device, bus_id);
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_22_READ;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = location;
+    BDK_BAR_WRITE(device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    smi_rd = __bdk_mdio_read_rd_dat(device, bus_id);
+    if (smi_rd.s.val)
+        return smi_rd.s.dat;
+    else
+        return -1;
+}
+
+
+/**
+ * Perform an MII write. This function is used to write PHY
+ * registers controlling auto negotiation.
+ *
+ * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
+ *                 support multiple busses.
+ * @param phy_id   The MII phy id
+ * @param location Register location to write
+ * @param val      Value to write
+ *
+ * @return -1 on error
+ *         0 on success
+ */
+int bdk_mdio_write(bdk_node_t node, int bus_id, int phy_id, int location, int val)
+{
+    const bdk_device_t *device = bdk_device_lookup(node, MDIO_DEVID, 0);
+    if (!device)
+    {
+        bdk_error("MDIO: ECAM device not found\n");
+        return -1;
+    }
+    bdk_smi_x_cmd_t smi_cmd;
+    bdk_smi_x_wr_dat_t smi_wr;
+
+    __bdk_mdio_set_clause22_mode(device, bus_id);
+
+    smi_wr.u = 0;
+    smi_wr.s.dat = val;
+    BDK_BAR_WRITE(device, BDK_SMI_X_WR_DAT(bus_id), smi_wr.u);
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_22_WRITE;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = location;
+    BDK_BAR_WRITE(device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    if (BDK_BAR_WAIT_FOR_FIELD(device, BDK_SMI_X_WR_DAT(bus_id), pending, ==, 0, BDK_MDIO_TIMEOUT))
+        return -1;
+
+    return 0;
+}
+
+/**
+ * Perform an IEEE 802.3 clause 45 MII read. This function is used to read PHY
+ * registers controlling auto negotiation.
+ *
+ * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
+ *                 support multiple busses.
+ * @param phy_id   The MII phy id
+ * @param device   MDIO Manageable Device (MMD) id
+ * @param location Register location to read
+ *
+ * @return Result from the read or -1 on failure
+ */
+
+int bdk_mdio_45_read(bdk_node_t node, int bus_id, int phy_id, int device, int location)
+{
+    const bdk_device_t *ecam_device = bdk_device_lookup(node, MDIO_DEVID, 0);
+    if (!ecam_device)
+    {
+        bdk_error("MDIO: ECAM device not found\n");
+        return -1;
+    }
+    bdk_smi_x_cmd_t smi_cmd;
+    bdk_smi_x_rd_dat_t smi_rd;
+    bdk_smi_x_wr_dat_t smi_wr;
+
+    __bdk_mdio_set_clause45_mode(ecam_device, bus_id);
+
+    smi_wr.u = 0;
+    smi_wr.s.dat = location;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_WR_DAT(bus_id), smi_wr.u);
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = device;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    if (BDK_BAR_WAIT_FOR_FIELD(ecam_device, BDK_SMI_X_WR_DAT(bus_id), pending, ==, 0, BDK_MDIO_TIMEOUT))
+    {
+        bdk_error("bdk_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d   TIME OUT(address)\n", bus_id, phy_id, device, location);
+        return -1;
+    }
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_45_READ;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = device;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    smi_rd = __bdk_mdio_read_rd_dat(ecam_device, bus_id);
+    if (smi_rd.s.pending)
+    {
+        bdk_error("bdk_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d   TIME OUT(data)\n", bus_id, phy_id, device, location);
+        return -1;
+    }
+
+    if (smi_rd.s.val)
+        return smi_rd.s.dat;
+    else
+    {
+        bdk_error("bdk_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d   INVALID READ\n", bus_id, phy_id, device, location);
+        return -1;
+    }
+}
+
+/**
+ * Perform an IEEE 802.3 clause 45 MII write. This function is used to write PHY
+ * registers controlling auto negotiation.
+ *
+ * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
+ *                 support multiple busses.
+ * @param phy_id   The MII phy id
+ * @param device   MDIO Manageable Device (MMD) id
+ * @param location Register location to write
+ * @param val      Value to write
+ *
+ * @return -1 on error
+ *         0 on success
+ */
+int bdk_mdio_45_write(bdk_node_t node, int bus_id, int phy_id, int device, int location,
+                                     int val)
+{
+    const bdk_device_t *ecam_device = bdk_device_lookup(node, MDIO_DEVID, 0);
+    if (!ecam_device)
+    {
+        bdk_error("MDIO: ECAM device not found\n");
+        return -1;
+    }
+    bdk_smi_x_cmd_t smi_cmd;
+    bdk_smi_x_wr_dat_t smi_wr;
+
+    __bdk_mdio_set_clause45_mode(ecam_device, bus_id);
+
+    smi_wr.u = 0;
+    smi_wr.s.dat = location;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_WR_DAT(bus_id), smi_wr.u);
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = device;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    if (BDK_BAR_WAIT_FOR_FIELD(ecam_device, BDK_SMI_X_WR_DAT(bus_id), pending, ==, 0, BDK_MDIO_TIMEOUT))
+        return -1;
+
+    smi_wr.u = 0;
+    smi_wr.s.dat = val;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_WR_DAT(bus_id), smi_wr.u);
+
+    smi_cmd.u = 0;
+    smi_cmd.s.phy_op = MDIO_CLAUSE_45_WRITE;
+    smi_cmd.s.phy_adr = phy_id;
+    smi_cmd.s.reg_adr = device;
+    BDK_BAR_WRITE(ecam_device, BDK_SMI_X_CMD(bus_id), smi_cmd.u);
+
+    if (BDK_BAR_WAIT_FOR_FIELD(ecam_device, BDK_SMI_X_WR_DAT(bus_id), pending, ==, 0, BDK_MDIO_TIMEOUT))
+        return -1;
+
+    return 0;
+}
+
+/**
+ * MDIO init() function
+ *
+ * @param device MDIO/SMI to initialize
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_mdio_init(bdk_node_t node)
+{
+    const bdk_device_t *device = bdk_device_lookup(node, MDIO_DEVID, 0);
+    if (!device)
+    {
+        bdk_error("MDIO: ECAM device not found\n");
+        return -1;
+    }
+    /* Change drive strength bits to fix issues when a QLM cable
+       is connected, creating a long spur path */
+    BDK_CSR_MODIFY(c, device->node, BDK_SMI_DRV_CTL,
+        c.s.pctl = 7; /* 30 ohm */
+        c.s.nctl = 7); /* 30 ohm */
+
+    for (int i = 0; i < 2; i++)
+        BDK_BAR_MODIFY(c, device, BDK_SMI_X_EN(i), c.s.en = 1);
+
+    return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-rnm.c b/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-rnm.c
index 8394ad8..c3a71f7 100644
--- a/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-rnm.c
+++ b/src/vendorcode/cavium/bdk/libbdk-driver/bdk-driver-rnm.c
@@ -22,7 +22,8 @@
 *
 * This Software, including technical data, may be subject to U.S. export
 * control laws, including the U.S. Export Administration Act and its
-* associated regulations, and may be subject to export or import
+* associateint bdk_rng_init(bdk_node_t node)
+* d regulations, and may be subject to export or import
 * regulations in other countries.
 *
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
@@ -40,6 +41,10 @@
 #include "libbdk-arch/bdk-csrs-pccpf.h"
 #include "libbdk-arch/bdk-csrs-rnm.h"
 
+#include <libbdk-hal/bdk-rng.h>
+#include <libbdk-hal/device/bdk-device.h>
+#define RNG_DEVID ((BDK_PCC_PROD_E_GEN << 24) | BDK_PCC_VENDOR_E_CAVIUM | (BDK_PCC_DEV_IDL_E_RNM << 16))
+
 BDK_REQUIRE_DEFINE(RNM);
 
 /**
@@ -84,32 +89,26 @@
 }
 
 /**
- * The RNM probe function
- *
- * @param device RNM to probe
- *
- * @return Zero on success, negative on failure
- */
-static int probe(bdk_device_t *device)
-{
-    bdk_device_rename(device, "N%d.RNM%d", device->node, device->instance);
-    return 0;
-}
-
-/**
  * RNM init() function
  *
  * @param device RNM to initialize
  *
  * @return Zero on success, negative on failure
  */
-static int init(bdk_device_t *device)
+int bdk_rng_init(bdk_node_t node)
 {
+    const bdk_device_t *device = bdk_device_lookup(node, RNG_DEVID, 0);
+    if (!device)
+    {
+        bdk_error("RNM: ECAM device not found\n");
+        return -1;
+    }
     BDK_BAR_MODIFY(c, device, BDK_RNM_CTL_STATUS,
         c.s.ent_en = 1;
         c.s.rng_en = 1);
-    /* Read back after enable so we know it is done. Needed on t88 pass 2.0 emulator */
+    /* Read back after enable so we know it is done. Needed on t88 pass 2.0 emulator and t81 real hardware !!!! */
     BDK_BAR_READ(device, BDK_RNM_CTL_STATUS);
+
     /* Errata (RNM-22528) First consecutive reads to RNM_RANDOM return same
        value. Before using the random entropy, read RNM_RANDOM at least once
        and discard the data */
@@ -117,8 +116,3 @@
     return 0;
 }
 
-bdk_driver_t __bdk_driver_rnm = {
-    .id = (BDK_PCC_PROD_E_GEN << 24) | BDK_PCC_VENDOR_E_CAVIUM | (BDK_PCC_DEV_IDL_E_RNM << 16),
-    .probe = probe,
-    .init = init,
-};
diff --git a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-access.c
similarity index 73%
copy from src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
copy to src/vendorcode/cavium/bdk/libbdk-hal/bdk-access.c
index 8cac04a..ebced00 100644
--- a/src/vendorcode/cavium/bdk/libbdk-arch/bdk-platform.c
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-access.c
@@ -37,23 +37,34 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include "libbdk-arch/bdk-csrs-ocla.h"
+#include "libbdk-arch/bdk-csrs-uaa.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
 
-bdk_platform_t __bdk_platform;
-
-void __bdk_platform_init()
+/**
+ * Perform a soft reset of the chip
+ *
+ * @return
+ */
+void bdk_reset_chip(bdk_node_t node)
 {
-    BDK_CSR_INIT(c, bdk_numa_master(), BDK_OCLAX_CONST(0));
-    if (c.u == 0)
+    fflush(NULL);
+
+    /* Wait for TX fifo to empty */
+    while (1)
     {
-        __bdk_platform = BDK_PLATFORM_ASIM;
+        BDK_CSR_INIT(fr, node, BDK_UAAX_FR(0));
+        if (fr.s.txfe)
+            break;
     }
-    else
-    {
-        int plat2 = bdk_fuse_read(bdk_numa_master(), 197);
-        int plat1 = bdk_fuse_read(bdk_numa_master(), 196);
-        int plat0 = bdk_fuse_read(bdk_numa_master(), 195);
-        __bdk_platform = (plat2 << 2) | (plat1 << 1) | plat0;
-    }
+
+    /* RST_OCX is not cleared by a chip reset. Clear it now to avoid repeated
+       resets due to CCPI state changes during reset */
+    BDK_CSR_WRITE(node, BDK_RST_OCX, 0);
+    BDK_CSR_READ(node, BDK_RST_OCX);
+
+    bdk_rst_soft_rst_t rst_soft_rst;
+    rst_soft_rst.u = 0;
+    rst_soft_rst.s.soft_rst = 1;
+    BDK_CSR_WRITE(node, BDK_RST_SOFT_RST, rst_soft_rst.u);
 }
 
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c
index f81285d..b8b0952 100644
--- a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c
@@ -37,123 +37,10 @@
 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
 ***********************license end**************************************/
 #include <bdk.h>
-#include "libbdk-arch/bdk-csrs-gti.h"
-#include "libbdk-arch/bdk-csrs-ocx.h"
-
-/**
- * Called in __bdk_init to setup the global timer
- */
-void bdk_clock_setup(bdk_node_t node)
-{
-    const bdk_node_t local_node = bdk_numa_local();
-
-    /* Check if the counter was already setup */
-    BDK_CSR_INIT(cntcr, node, BDK_GTI_CC_CNTCR);
-    if (cntcr.s.en)
-        return;
-
-    /* Configure GTI to tick at BDK_GTI_RATE */
-    uint64_t sclk = bdk_clock_get_rate(node, BDK_CLOCK_SCLK);
-    uint64_t inc = (BDK_GTI_RATE << 32) / sclk;
-    BDK_CSR_WRITE(node, BDK_GTI_CC_CNTRATE, inc);
-    BDK_CSR_WRITE(node, BDK_GTI_CTL_CNTFRQ, BDK_GTI_RATE);
-    cntcr.s.en = 1;
-    if (node != local_node)
-    {
-        /* Synchronize with local node. Very simple set of counter, will be
-           off a little */
-        BDK_CSR_WRITE(node, BDK_GTI_CC_CNTCV, bdk_clock_get_count(BDK_CLOCK_TIME));
-    }
-    /* Enable the counter */
-    BDK_CSR_WRITE(node, BDK_GTI_CC_CNTCR, cntcr.u);
-    BDK_CSR_READ(node, BDK_GTI_CC_CNTCR);
-
-    if (node != local_node)
-    {
-        if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
-        {
-            /* Assume the delay in each direction is the same, sync the counters */
-            int64_t local1 = bdk_clock_get_count(BDK_CLOCK_TIME);
-            int64_t remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTCV);
-            int64_t local2 = bdk_clock_get_count(BDK_CLOCK_TIME);
-            int64_t expected = (local1 + local2) / 2;
-            BDK_CSR_WRITE(node, BDK_GTI_CC_CNTADD, expected - remote);
-            BDK_TRACE(INIT, "N%d.GTI: Clock synchronization with master\n"
-                "    expected: %ld, remote %ld\n"
-                "    Counter correction: %ld\n",
-                node, expected, remote, expected - remote);
-        }
-        else
-        {
-            /* Due to errata TBD, we need to use OCX_PP_CMD to write
-               GTI_CC_CNTMB in order for timestamps to update. These constants
-               are the addresses we need for both local and remote GTI_CC_CNTMB */
-            const uint64_t LOCAL_GTI_CC_CNTMB = bdk_numa_get_address(local_node, BDK_GTI_CC_CNTMB);
-            const uint64_t REMOTE_GTI_CC_CNTMB = bdk_numa_get_address(node, BDK_GTI_CC_CNTMB);
-            /* Build partial OCX_PP_CMD command used for writes. Address will
-               be filled later */
-            BDK_CSR_DEFINE(pp_cmd, BDK_OCX_PP_CMD);
-            pp_cmd.u = 0;
-            pp_cmd.s.wr_mask = 0xff;
-
-            const int NUM_AVERAGE = 16; /* Choose a power of two to avoid division */
-            int64_t local_to_remote_sum = 0;
-            int64_t local_to_remote_min = 1000000;
-            int64_t local_to_remote_max = -1000000;
-            int64_t remote_to_local_sum = 0;
-            int64_t remote_to_local_min = 1000000;
-            int64_t remote_to_local_max = -1000000;
-            for (int loop = 0; loop < NUM_AVERAGE; loop++)
-            {
-                /* Perform a write to the remote GTI_CC_CNTMB to cause timestamp
-                   update. We don't care about the value actually written */
-                pp_cmd.s.addr = REMOTE_GTI_CC_CNTMB;
-                BDK_CSR_WRITE(local_node, BDK_OCX_PP_CMD, pp_cmd.u);
-                BDK_CSR_READ(local_node, BDK_OCX_PP_CMD);
-
-                int64_t remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTMBTS);
-                int64_t local = BDK_CSR_READ(local_node, BDK_GTI_CC_CNTMBTS);
-                int64_t delta = remote - local;
-
-                local_to_remote_sum += delta;
-                if (delta < local_to_remote_min)
-                    local_to_remote_min = delta;
-                if (delta > local_to_remote_max)
-                    local_to_remote_max = delta;
-
-                /* Perform a write to the local GTI_CC_CNTMB to cause timestamp
-                   update. We don't care about the value actually written */
-                pp_cmd.s.addr = LOCAL_GTI_CC_CNTMB;
-                BDK_CSR_WRITE(node, BDK_OCX_PP_CMD, pp_cmd.u);
-                BDK_CSR_READ(node, BDK_OCX_PP_CMD);
-
-                remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTMBTS);
-                local = BDK_CSR_READ(local_node, BDK_GTI_CC_CNTMBTS);
-                delta = local - remote;
-
-                remote_to_local_sum += delta;
-                if (delta < remote_to_local_min)
-                    remote_to_local_min = delta;
-                if (delta > remote_to_local_max)
-                    remote_to_local_max = delta;
-            }
-            /* Calculate average, rounding to nearest */
-            int64_t local_to_remote = (local_to_remote_sum + NUM_AVERAGE/2) / NUM_AVERAGE;
-            int64_t remote_to_local = (remote_to_local_sum + NUM_AVERAGE/2) / NUM_AVERAGE;
-            /* Calculate remote node offset */
-            int64_t remote_offset = (remote_to_local - local_to_remote) / 2;
-            BDK_CSR_WRITE(node, BDK_GTI_CC_CNTADD, remote_offset);
-            BDK_TRACE(INIT, "N%d.GTI: Clock synchronization with master\n"
-                "    local -> remote: min %ld, avg %ld, max %ld\n"
-                "    remote -> local: min %ld, avg %ld, max %ld\n"
-                "    Counter correction: %ld\n",
-                node,
-                local_to_remote_min, local_to_remote, local_to_remote_max,
-                remote_to_local_min, remote_to_local, remote_to_local_max,
-                remote_offset);
-        }
-    }
-}
+#include <libbdk-arch/bdk-csrs-gti.h>
+#include <libbdk-arch/bdk-csrs-ocx.h>
+#include <libbdk-hal/bdk-clock.h>
+#include <libbdk-arch/bdk-csrs-rst.h>
 
 /**
  * Get cycle count based on the clock type.
@@ -165,12 +52,6 @@
 {
     bdk_node_t node = bdk_numa_local();
     BDK_CSR_INIT(rst_boot, node, BDK_RST_BOOT);
-    if (bdk_is_platform(BDK_PLATFORM_EMULATOR))
-    {
-        /* Force RCLK and SCLK to be 1GHz on emulator */
-        rst_boot.s.c_mul = 20;
-        rst_boot.s.pnr_mul = 20;
-    }
     uint64_t ref_cntr = BDK_CSR_READ(node, BDK_RST_REF_CNTR);
     switch(clock)
     {
@@ -199,12 +80,6 @@
     const uint64_t REF_CLOCK = 50000000;
 
     BDK_CSR_INIT(mio_rst_boot, node, BDK_RST_BOOT);
-    if (bdk_is_platform(BDK_PLATFORM_EMULATOR))
-    {
-        /* Force RCLK and SCLK to be 1GHz on emulator */
-        mio_rst_boot.s.c_mul = 20;
-        mio_rst_boot.s.pnr_mul = 20;
-    }
     switch (clock)
     {
         case BDK_CLOCK_TIME:
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-config.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-config.c
index d4b412d..91f05d3 100644
--- a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-config.c
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-config.c
@@ -1,81 +1,265 @@
-/***********************license start***********************************
-* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
-* reserved.
-*
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*
-*   * Redistributions of source code must retain the above copyright
-*     notice, this list of conditions and the following disclaimer.
-*
-*   * Redistributions in binary form must reproduce the above
-*     copyright notice, this list of conditions and the following
-*     disclaimer in the documentation and/or other materials provided
-*     with the distribution.
-*
-*   * Neither the name of Cavium Inc. nor the names of
-*     its contributors may be used to endorse or promote products
-*     derived from this software without specific prior written
-*     permission.
-*
-* This Software, including technical data, may be subject to U.S. export
-* control laws, including the U.S. Export Administration Act and its
-* associated regulations, and may be subject to export or import
-* regulations in other countries.
-*
-* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
-* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
-* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
-* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
-* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
-* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
-* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
-* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
-* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
-* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
-***********************license end**************************************/
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+ * reserved.
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * This file consists of data imported from bdk-config.c
+ */
+
 #include <bdk.h>
-#include <stdarg.h>
-#include <libfdt.h>
-#include <unistd.h>
-#include "libbdk-arch/bdk-csrs-mio_fus.h"
-#include "libbdk-arch/bdk-csrs-fus.h"
+#include <libbdk-hal/bdk-config.h>
+#include <string.h>
+#include <assert.h>
+#include <lame_string.h>
 
-/* Set this define to override the trace the BDK uses. This is most
-   useful with trusted boot when the setup menus are not able to
-   configure the trace level. A possible example: */
-//#define BDK_TRACE_OVERRIDE (1ull << BDK_TRACE_ENABLE_INIT)
-#define BDK_TRACE_OVERRIDE 0
+static struct bdk_devicetree_key_value *config_fdt;
 
-typedef enum
+#if !defined(__PRE_RAM__)
+static struct bdk_devicetree_key_value *bdk_config_duplicate(
+        const struct bdk_devicetree_key_value *old,
+        size_t free_space)
 {
-    BDK_CONFIG_TYPE_INT,
-    BDK_CONFIG_TYPE_STR,
-    BDK_CONFIG_TYPE_STR_LIST,
-    BDK_CONFIG_TYPE_BINARY,
-} bdk_config_type_t;
+    struct bdk_devicetree_key_value *new;
+    size_t len = sizeof(struct bdk_devicetree_key_value) + free_space;
+    const struct bdk_devicetree_key_value *iter = old;
+    while (iter->key) {
+       iter++;
+       len += sizeof(struct bdk_devicetree_key_value);
+    }
+    new = malloc(len);
+    if (!new)
+       return NULL;
 
-typedef struct
+    memcpy(new, old, len);
+
+    return new;
+}
+#endif
+/**
+ * Set the device tree used for configuration
+ *
+ * @param fdt    Device tree to use. Memory is assumed to be from malloc() and bdk_config takes
+ *               over ownership on success
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_config_set_fdt(const struct bdk_devicetree_key_value *fdt)
 {
-    const char *format;     /* Printf style format string to create the item name */
-    const bdk_config_type_t ctype;/* Type of this item */
-    int64_t default_value;  /* Default value when no present. String defaults are cast to pointers from this */
-    const int64_t min_value;/* Minimum valid value for INT parameters. Unused for Strings */
-    const int64_t max_value;/* Maximum valid value for INT parameters. Unused for Strings */
-} bdk_config_info_t;
+#if !defined(__PRE_RAM__)
+    config_fdt = bdk_config_duplicate(fdt, 0);
+#else
+    config_fdt = (void *)fdt;
+#endif
+    return 0;
+}
 
-static void config_set_defaults(void);
+/**
+ * Look up a configuration item in the environment and replace it.
+ *
+ * @param name
+ *
+ * @return
+ */
+static void set_value(const char *name, const char *val)
+{
+#if !defined(__PRE_RAM__)
+    struct bdk_devicetree_key_value *iter;
+    char n[64];
 
-/* Tracing defaults to the level specified here before config files are loaded */
-uint64_t bdk_trace_enables = BDK_TRACE_OVERRIDE;
+    strncpy(n, name, sizeof(n));
+    n[sizeof(n)-1] = '\0';
 
-/* Global variables that contain the config inside a FDT */
-static void *config_fdt;
-static int config_node;
+    iter = config_fdt;
+    while (iter->key) {
+        if (strcmp(iter->key, n) == 0) {
+            // we are leaking memory here...
+            iter->value = (const char *)strdup(val);
+            return;
+        }
+        iter++;
+    }
+    /* Not found: Create one */
+    iter = bdk_config_duplicate(config_fdt,
+       sizeof(struct bdk_devicetree_key_value));
+    if (!iter)
+        return;
 
-static bdk_config_info_t config_info[__BDK_CONFIG_END] = {
+    free(config_fdt);
+    config_fdt = iter;
+    while (iter->key) {
+        iter++;
+    }
+    iter->key = (const char *)strdup(name);
+    iter->value = (const char *)strdup(val);
+    iter++;
+    iter->key = 0;
+    iter->value = 0;
+#endif
+}
+
+/**
+ * Look up a configuration item in the environment.
+ *
+ * @param name
+ *
+ * @return
+ */
+static const char *get_value(const char *name)
+{
+    const struct bdk_devicetree_key_value *iter;
+    char n[64];
+
+    strncpy(n, name, sizeof(n));
+    n[sizeof(n)-1] = '\0';
+
+    while (*n) {
+        iter = config_fdt;
+        while (iter->key) {
+            if (strcmp(iter->key, n) == 0)
+                return iter->value;
+            iter++;
+        }
+
+        char *p = strrchr(n, '.');
+        if (p)
+            *p = '\0';
+        else
+            break;
+    }
+    return NULL;
+}
+
+/**
+ * Get an integer configuration item
+ *
+ * @param cfg_item  Config item to get. If the item takes parameters (see bdk_config_t), then the
+ *                  parameters are listed following cfg_item.
+ *
+ * @return The value of the configuration item, or def_value if the item is not set
+ */
+int64_t bdk_config_get_int(bdk_config_t cfg_item, ...)
+{
+    char name[64];
+    size_t count;
+    int64_t tmp;
+
+    assert(cfg_item < __BDK_CONFIG_END);
+
+    /* Make sure the correct access function was called */
+    assert(config_info[cfg_item].ctype == BDK_CONFIG_TYPE_INT);
+
+    if (!config_fdt)
+        return config_info[cfg_item].default_value;
+
+    va_list args;
+    va_start(args, cfg_item);
+    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
+    va_end(args);
+
+    const char *val = get_value(name);
+    if (!val)
+        return config_info[cfg_item].default_value;
+
+#if 0
+        if ((val[0] == '0') && (val[1] == 'x'))
+            count = sscanf(val + 2, "%lx", &tmp);
+        else
+            count = sscanf(val, "%li", &tmp);
+#endif
+
+    if ((val[0] == '0') && (val[1] == 'x'))
+        count = str_to_hex(val + 2, &tmp);
+    else
+        count = str_to_int(val, &tmp);
+    if (count == 1) {
+        if ((tmp < config_info[cfg_item].min_value) ||
+            (tmp > config_info[cfg_item].max_value)) {
+            printk(BIOS_WARNING, "Out of range for %s = %s, using "
+                   "default\n", name, val);
+            return config_info[cfg_item].default_value;
+        }
+        return tmp;
+    }
+
+    printk(BIOS_WARNING, "Failed to parse %s = %s, using default\n",
+           name, val);
+    return config_info[cfg_item].default_value;
+}
+
+/**
+ * Set an integer configuration item. Note this only sets the item in memory,
+ * persistent storage is not updated.
+ *
+ * @param value    Configuration item value
+ * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
+ *                 parameters are listed following cfg_item.
+ */
+void bdk_config_set_int(int64_t value, bdk_config_t cfg_item, ...)
+{
+    char name[64], val[32];
+
+    assert(cfg_item < __BDK_CONFIG_END);
+
+    /* Make sure the correct access function was called */
+    assert(config_info[cfg_item].ctype == BDK_CONFIG_TYPE_INT);
+
+    if (!config_fdt)
+        return;
+
+    va_list args;
+    va_start(args, cfg_item);
+    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
+    va_end(args);
+
+    snprintf(val, sizeof(val), "0x%016llx", value);
+    set_value(name, val);
+}
+
+/**
+ * Get a string configuration item
+ *
+ * @param cfg_item  Config item to get. If the item takes parameters (see bdk_config_t), then the
+ *                  parameters are listed following cfg_item.
+ *
+ * @return The value of the configuration item, or def_value if the item is not set
+ */
+const char *bdk_config_get_str(bdk_config_t cfg_item, ...)
+{
+    char name[64];
+
+    /* Make sure the correct access function was called */
+    assert(config_info[cfg_item].ctype == BDK_CONFIG_TYPE_STR);
+
+    if (!config_fdt)
+        return (const char *)config_info[cfg_item].default_value;
+
+    va_list args;
+    va_start(args, cfg_item);
+    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
+
+    if (BDK_CONFIG_QLM_MODE == cfg_item) {
+        char name2[64];
+        vsnprintf(name2, sizeof(name2)-1,"QLM-MODE.N%d.DLM%d" , args);
+        const char *val = get_value(name2);
+        if (val)
+            printk(BIOS_WARNING, "%s: QLM-MODE.N%%d.DLM%%d format "
+                   "depricated. Please use QLM-MODE.N%%d.QLM%%d "
+                   "instead\n", name2);
+    }
+    va_end(args);
+
+    const char *val = get_value(name);
+    if (val)
+        return val;
+    else
+        return (const char *)config_info[cfg_item].default_value;
+}
+
+bdk_config_info_t config_info[] = {
     /* Board manufacturing data */
     [BDK_CONFIG_BOARD_MODEL] = {
         .format = "BOARD-MODEL", /* String, No parameters */
@@ -251,11 +435,11 @@
         .max_value = 0xffff,
     },
     [BDK_CONFIG_PCIE_WIDTH] = {
-	.format = "PCIE-WIDTH.N%d.PORT%d", /* Parameters: Node, Port */
-	.ctype = BDK_CONFIG_TYPE_INT,
-	.default_value = -1, /* Width override for PCIe links */
-	.min_value = -1,
-	.max_value = 16,
+    .format = "PCIE-WIDTH.N%d.PORT%d", /* Parameters: Node, Port */
+    .ctype = BDK_CONFIG_TYPE_INT,
+    .default_value = -1, /* Width override for PCIe links */
+    .min_value = -1,
+    .max_value = 16,
     },
     [BDK_CONFIG_PCIE_PHYSICAL_SLOT] = {
         .format = "PCIE-PHYSICAL-SLOT.N%d.PORT%d", /* Parameters: Node, Port */
@@ -264,6 +448,13 @@
         .min_value = -1,
         .max_value = 8191,
     },
+    [BDK_CONFIG_PCIE_SKIP_LINK_TRAIN] = {
+            .format = "PCIE-SKIP-LINK-TRAIN.N%d.PORT%d", /* Parameters: Node, Port */
+            .ctype = BDK_CONFIG_TYPE_INT,
+            .default_value = 0, /* Define which physical slot we connect to on the board */
+            .min_value = 0,
+            .max_value = 1,
+        },
     [BDK_CONFIG_PCIE_FLASH] = {
         .format = "PCIE-FLASH.N%d.PORT%d", /* Parameters: Node, Port */
         .ctype = BDK_CONFIG_TYPE_STR_LIST,
@@ -361,7 +552,6 @@
         .min_value = -1,
         .max_value = 40,
     },
-
     /* DRAM configuration options */
     [BDK_CONFIG_DDR_SPEED] = {
         .format = "DDR-SPEED.N%d", /* Parameters: Node */
@@ -862,1085 +1052,3 @@
     },
 };
 
-/**
- * Look up a configuration item in the environment.
- *
- * @param name
- *
- * @return
- */
-static const char *get_value(const char *name, int *blob_size)
-{
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config asked for %s before configuration loaded\n", name);
-        return NULL;
-    }
-
-    char n[64];
-    strncpy(n, name, sizeof(n));
-    n[sizeof(n)-1] = '\0';
-
-    while (*n)
-    {
-        const char *val = fdt_getprop(config_fdt, config_node, n, blob_size);
-        if (val)
-            return val;
-
-        char *p = strrchr(n, '.');
-        if (p)
-            *p = '\0';
-        else
-            break;
-    }
-    return NULL;
-}
-
-/**
- * Get an integer configuration item
- *
- * @param cfg_item  Config item to get. If the item takes parameters (see bdk_config_t), then the
- *                  parameters are listed following cfg_item.
- *
- * @return The value of the configuration item, or def_value if the item is not set
- */
-int64_t bdk_config_get_int(bdk_config_t cfg_item, ...)
-{
-    /* Make sure the correct access function was called */
-    if (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_INT)
-        bdk_fatal("bdk_config_get_int() called for %s, not an int\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    va_list args;
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    va_end(args);
-
-    const char *val = get_value(name, NULL);
-    if (val)
-    {
-        int count;
-        int64_t tmp;
-        if ((val[0] == '0') && (val[1] == 'x'))
-            count = sscanf(val + 2, "%lx", &tmp);
-        else
-            count = sscanf(val, "%li", &tmp);
-        if (count == 1)
-        {
-            if ((tmp < config_info[cfg_item].min_value) || (tmp > config_info[cfg_item].max_value))
-            {
-                bdk_warn("Out of range for %s = \"%s\", using default\n", name, val);
-                return config_info[cfg_item].default_value;
-            }
-            return tmp;
-        }
-        else
-        {
-            bdk_warn("Failed to parse %s = \"%s\", using default\n", name, val);
-            return config_info[cfg_item].default_value;
-        }
-    }
-    else
-        return config_info[cfg_item].default_value;
-}
-
-/**
- * Get a string configuration item
- *
- * @param cfg_item  Config item to get. If the item takes parameters (see bdk_config_t), then the
- *                  parameters are listed following cfg_item.
- *
- * @return The value of the configuration item, or def_value if the item is not set
- */
-const char *bdk_config_get_str(bdk_config_t cfg_item, ...)
-{
-    /* Make sure the correct access function was called */
-    if (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_STR)
-        bdk_fatal("bdk_config_get_str() called for %s, not a str\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    va_list args;
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    
-    if (BDK_CONFIG_QLM_MODE == cfg_item)
-    {
-    	char name2[64];
-    	vsnprintf(name2, sizeof(name2)-1,"QLM-MODE.N%d.DLM%d" , args);
-	const char *val = get_value(name2, NULL);
-	if (val)
-        	bdk_warn("%s: QLM-MODE.N%%d.DLM%%d format depricated. Please use QLM-MODE.N%%d.QLM%%d instead\n", name2);
-	
-    }
-    va_end(args);
-
-    const char *val = get_value(name, NULL);
-    if (val)
-        return val;
-    else
-        return (const char *)config_info[cfg_item].default_value;
-}
-
-/**
- * Get a binary blob
- *
- * @param blob_size Integer to receive the size of the blob
- * @param cfg_item  Config item to get. If the item takes parameters (see bdk_config_t), then the
- *                  parameters are listed following cfg_item.
- *
- * @return The value of the configuration item, or def_value if the item is not set
- */
-const void* bdk_config_get_blob(int *blob_size, bdk_config_t cfg_item, ...)
-{
-    char name[64];
-    va_list args;
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    va_end(args);
-
-    const void *val = get_value(name, blob_size);
-    if (val)
-        return val;
-    else
-        return (const void *)config_info[cfg_item].default_value;
-}
-
-/**
- * Set an integer configuration item. Note this only sets the item in memory,
- * persistent storage is not updated. The optional parameters for the setting are
- * not supplied, meaning this function only changes the global default.
- *
- * @param value    Configuration item value
- * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
- *                 parameters are listed following cfg_item.
- */
-void bdk_config_set_int_no_param(int64_t value, bdk_config_t cfg_item)
-{
-    /* Make sure the correct access function was called */
-    if (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_INT)
-        bdk_fatal("bdk_config_set_int_no_param() called for %s, not an int\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    char valstr[20];
-    /* Create a name without the optional parameters */
-    strncpy(name, config_info[cfg_item].format, sizeof(name) - 1);
-    name[sizeof(name) - 1] = 0;
-    char *ptr = strchr(name, '.');
-    if (ptr)
-        *ptr = 0;
-
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config set %s before configuration loaded\n", name);
-        return;
-    }
-    if ((value < config_info[cfg_item].min_value) || (value > config_info[cfg_item].max_value))
-    {
-        bdk_error("Set out of range for %s = \"0x%lx\", ignoring\n", name, value);
-        return;
-    }
-
-    if (value < 10)
-        snprintf(valstr, sizeof(valstr), "%ld", value);
-    else
-        snprintf(valstr, sizeof(valstr), "0x%lx", value);
-
-    int status = fdt_setprop_string(config_fdt, config_node, name, valstr);
-    if (status < 0)
-        bdk_fatal("Failed to set %s=%s in FDT\n", name, valstr);
-}
-
-/**
- * Set an integer configuration item. Note this only sets the item in memory,
- * persistent storage is not updated.
- *
- * @param value    Configuration item value
- * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
- *                 parameters are listed following cfg_item.
- */
-void bdk_config_set_int(int64_t value, bdk_config_t cfg_item, ...)
-{
-    /* Make sure the correct access function was called */
-    if (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_INT)
-        bdk_fatal("bdk_config_set_int() called for %s, not an int\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    char valstr[20];
-    va_list args;
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    va_end(args);
-
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config set %s before configuration loaded\n", name);
-        return;
-    }
-    if ((value < config_info[cfg_item].min_value) || (value > config_info[cfg_item].max_value))
-    {
-        bdk_error("Set out of range for %s = \"0x%lx\", ignoring\n", name, value);
-        return;
-    }
-
-    if (value < 10)
-        snprintf(valstr, sizeof(valstr), "%ld", value);
-    else
-        snprintf(valstr, sizeof(valstr), "0x%lx", value);
-
-    int status = fdt_setprop_string(config_fdt, config_node, name, valstr);
-    if (status < 0)
-        bdk_fatal("Failed to set %s=%s in FDT\n", name, valstr);
-}
-
-/**
- * Set an integer configuration item. Note this only sets the item in memory,
- * persistent storage is not updated.
- *
- * @param value    Configuration item value
- * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
- *                 parameters are listed following cfg_item.
- */
-void bdk_config_set_str(const char *value, bdk_config_t cfg_item, ...)
-{
-    /* Make sure the correct access function was called */
-    if (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_STR)
-        bdk_fatal("bdk_config_set_str() called for %s, not a str\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    va_list args;
-
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    va_end(args);
-
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config set %s before configuration loaded\n", name);
-        return;
-    }
-
-    int status;
-    if (value)
-        status = fdt_setprop_string(config_fdt, config_node, name, value);
-    else
-        status = fdt_delprop(config_fdt, config_node, name);
-
-    if ((status < 0) && (status != -FDT_ERR_NOTFOUND))
-        bdk_fatal("Failed to set %s=%s in FDT\n", name, value);
-}
-
-/**
- * Set a blob configuration item. Note this only sets the
- * item in memory, persistent storage is not updated. The optional
- * parameters for the setting are not supplied, meaning this function
- * only changes the global default.
- *
- * @param size     Size of the item in bytes. A size of zero removes the device tree field
- * @param value    Configuration item value
- * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
- *                 parameters are listed following cfg_item.
- */
-void bdk_config_set_blob_no_param(int size, const void *value, bdk_config_t cfg_item)
-{
-    /* Make sure the correct access function was called */
-    if ((config_info[cfg_item].ctype != BDK_CONFIG_TYPE_BINARY) &&
-        (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_STR_LIST))
-        bdk_fatal("bdk_config_set_blob() called for %s, not binary\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    /* Create a name without the optional parameters */
-    strncpy(name, config_info[cfg_item].format, sizeof(name) - 1);
-    name[sizeof(name) - 1] = 0;
-    char *ptr = strchr(name, '.');
-    if (ptr)
-        *ptr = 0;
-
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config set %s before configuration loaded\n", name);
-        return;
-    }
-
-    int status;
-    if (size)
-        status = fdt_setprop(config_fdt, config_node, name, value, size);
-    else
-        status = fdt_delprop(config_fdt, config_node, name);
-
-    if ((status < 0) && (status != -FDT_ERR_NOTFOUND))
-        bdk_fatal("Failed to set %s in FDT\n", name);
-}
-
-/**
- * Set a blob configuration item. Note this only sets the
- * item in memory, persistent storage is not updated.
- *
- * @param size     Size of the item in bytes. A size of zero removes the device tree field
- * @param value    Configuration item value
- * @param cfg_item Config item to set. If the item takes parameters (see bdk_config_t), then the
- *                 parameters are listed following cfg_item.
- */
-void bdk_config_set_blob(int size, const void *value, bdk_config_t cfg_item, ...)
-{
-    /* Make sure the correct access function was called */
-    if ((config_info[cfg_item].ctype != BDK_CONFIG_TYPE_BINARY) &&
-        (config_info[cfg_item].ctype != BDK_CONFIG_TYPE_STR_LIST))
-        bdk_fatal("bdk_config_set_blob() called for %s, not binary\n",
-            config_info[cfg_item].format);
-
-    char name[64];
-    va_list args;
-
-    va_start(args, cfg_item);
-    vsnprintf(name, sizeof(name)-1, config_info[cfg_item].format, args);
-    va_end(args);
-
-    if (!config_fdt)
-    {
-        bdk_error("bdk-config set %s before configuration loaded\n", name);
-        return;
-    }
-
-    int status;
-    if (size)
-        status = fdt_setprop(config_fdt, config_node, name, value, size);
-    else
-        status = fdt_delprop(config_fdt, config_node, name);
-
-    if ((status < 0) && (status != -FDT_ERR_NOTFOUND))
-        bdk_fatal("Failed to set %s in FDT\n", name);
-}
-
-/**
- * Multiple functions need to display the config item help string in a format
- * suitable for inclusion in a device tree. This function displays the help
- * message properly indented and such.
- *
- * @param cfg    Config item to display help for
- */
-static void display_help(bdk_config_t cfg)
-{
-    /* Print the help text as a comment before the entry */
-    /* Indent with tabs like Linux requires */
-    printf("\n");
-    printf("\t/* ");
-    const char *ptr = bdk_config_get_help(cfg);
-    while (*ptr)
-    {
-        putchar(*ptr);
-        if (*ptr == '\n')
-            putchar('\t');
-        ptr++;
-    }
-    printf(" */\n");
-    /* Print the parameter and its default value a comment. This will be
-       a reference that is easy for the user to change */
-    printf("\t//%s = ", config_info[cfg].format);
-    switch (config_info[cfg].ctype)
-    {
-        case BDK_CONFIG_TYPE_INT:
-            if (config_info[cfg].default_value < 10)
-                printf("\"%ld\"", config_info[cfg].default_value);
-            else
-                printf("\"0x%lx\"", config_info[cfg].default_value);
-            break;
-        case BDK_CONFIG_TYPE_STR:
-        case BDK_CONFIG_TYPE_STR_LIST:
-            if (config_info[cfg].default_value)
-                printf("\"%s\"", (const char *)config_info[cfg].default_value);
-            else
-                printf("\"\"");
-            break;
-        case BDK_CONFIG_TYPE_BINARY:
-            printf("[]");
-        break;
-    }
-    printf(";\n");
-}
-
-/**
- * Display the active configuration as a valid device tree
- */
-void bdk_config_show(void)
-{
-    /* Output the standard DTS headers */
-    printf("/dts-v1/;\n");
-    printf("\n");
-    printf("/ {\n");
-    printf("cavium,bdk {\n");
-    for (bdk_config_t cfg = 0; cfg < __BDK_CONFIG_END; cfg++)
-    {
-        /* Show the help message */
-        display_help(cfg);
-
-        /* Figure out how much of the config item is fixed versus
-           the optional parameters */
-        const char *format = config_info[cfg].format;
-        const char *format_param = strchr(format, '.');
-        int format_length = 0;
-        if (format_param)
-            format_length = format_param - format;
-
-        /* Loop through all device tree entries displaying the ones that
-           match this format */
-        int offset = fdt_first_property_offset(config_fdt, config_node);
-        while (offset >= 0)
-        {
-            /* Get the device tree item */
-            const char *name = NULL;
-            int data_size = 0;
-            const char *data = fdt_getprop_by_offset(config_fdt, offset, &name, &data_size);
-            const char *data_end = data + data_size;
-            /* Find the first param */
-            const char *name_param = strchr(name, '.');
-            int name_length = 0;
-            if (name_param)
-            {
-                /* We want to compare up to the first param */
-                name_length = name_param - name;
-                /* If the lengths are different not including the parameters,
-                   then we force a full matchn which will always fail */
-                if (name_length != format_length)
-                    name_length = 0;
-            }
-            else /* No params, match base of format */
-                name_length = format_length;
-
-            /* Check if it matches the current config format */
-            int match;
-            if (name_length)
-            {
-                /* Check the prefix */
-                match = strncmp(name, format, name_length);
-                if (match == 0)
-                {
-                    /* Prefix matched. We only really match if the next
-                       character is the end of the string or a '.' */
-                    if ((name[name_length] != 0) && (name[name_length] != '.'))
-                        match = 1;
-                }
-            }
-            else
-                match = strcmp(name, format);
-            /* Print matching entries */
-            if (match == 0)
-            {
-                if (config_info[cfg].ctype == BDK_CONFIG_TYPE_BINARY)
-                {
-                    printf("\t%s = [", name);
-                    const char *ptr = data;
-                    while (ptr < data_end)
-                    {
-                        printf(" %02x", (int)*ptr);
-                        ptr++;
-                    }
-                    printf(" ]");
-                }
-                else
-                {
-                    printf("\t%s = \"%s\"", name, data);
-                    data += strlen(data) + 1;
-                    while (data < data_end)
-                    {
-                        printf(",\n\t\t\"%s\"", data);
-                        data += strlen(data) + 1;
-                    }
-                }
-                printf(";\n");
-            }
-            offset = fdt_next_property_offset(config_fdt, offset);
-        }
-    }
-    /* Output the standard DTS footers */
-    printf("}; /* cavium,bdk */\n");
-    printf("}; /* / */\n");
-}
-
-/**
- * Display a list of all possible config items with help text
- */
-void bdk_config_help(void)
-{
-    /* Write out formatted as part of a device tree source (dts) file */
-    printf("/dts-v1/;\n");
-    printf("\n");
-    printf("/ {\n");
-    printf("cavium,bdk {\n");
-    for (bdk_config_t cfg = 0; cfg < __BDK_CONFIG_END; cfg++)
-        display_help(cfg);
-    printf("}; /* cavium,bdk */\n");
-    printf("}; /* / */\n");
-}
-
-
-/**
- * Save the current configuration to flash
- *
- * @return Zero on success, negative on failure
- */
-int bdk_config_save(void)
-{
-    /* Pack the FDT so it uses less space */
-    int status = fdt_pack(config_fdt);
-    if (status < 0)
-    {
-        bdk_error("FDT error %d: %s\n", status, fdt_strerror(status));
-        return -1;
-    }
-
-    /* Calculate a CRC32 of the FDT */
-    int fdt_size = fdt_totalsize(config_fdt);
-    uint32_t crc32 = bdk_crc32(config_fdt, fdt_size, 0);
-
-    /* Open the output file */
-    FILE *outf = fopen("/fatfs/default.dtb", "wb");
-    if (!outf)
-    {
-        bdk_error("Failed to open flash");
-        return -1;
-    }
-
-    /* Write the FDT */
-    if (fwrite(config_fdt, fdt_size, 1, outf) != 1)
-    {
-        bdk_error("Failed to write FDT");
-        fclose(outf);
-        return -1;
-    }
-
-    /* Save the CRC32 in the same endianness as the FDT */
-    crc32 = cpu_to_fdt32(crc32);
-    if (fwrite(&crc32, sizeof(crc32), 1, outf) != 1)
-    {
-        bdk_error("Failed to write FDT CRC32");
-        fclose(outf);
-        return -1;
-    }
-
-    fclose(outf);
-    return 0;
-}
-
-/**
- * Takes the current live device tree and exports it to a memory address suitable
- * for passing to the next binary in register X1.
- *
- * @return Physical address of the device tree, or 0 on failure
- */
-uint64_t __bdk_config_export_to_mem(void)
-{
-    void *end_ptr = sbrk(0);
-    bdk_node_t node = bdk_numa_master();
-    int fdt_size = fdt_totalsize(config_fdt);
-
-    /* Round size up to 4KB boundary, be sure to add 4 bytes for CRC32 */
-    int fdt_space = (fdt_size + 4 + 0xfff) & -4096;
-    /* First try 4MB - FDT size as this keeps the FDT in the 4MB secure space
-        setup by ATF */
-    void *fdt_ptr = bdk_phys_to_ptr(0x400000 - fdt_space);
-    if (!__bdk_is_dram_enabled(node))
-    {
-        /* Address must be in L2 */
-        int l2_size = bdk_l2c_get_cache_size_bytes(node);
-        void *l2_ptr = bdk_phys_to_ptr(l2_size - fdt_space);
-        if (l2_ptr < fdt_ptr)
-            fdt_ptr = l2_ptr;
-        if (fdt_ptr < end_ptr)
-        {
-            bdk_error("No room for FDT to pass to next binary\n");
-            return 0;
-        }
-    }
-    else
-    {
-        /* We have DRAM, make sure we're past the end of this image */
-        if (fdt_ptr < end_ptr)
-            fdt_ptr = end_ptr;
-    }
-    uint32_t crc32 = bdk_crc32(config_fdt, fdt_size, 0);
-    fdt_move(config_fdt, fdt_ptr, fdt_size);
-    /* CRC32 is stored in same endianness as FDT at the end */
-    *(uint32_t *)((const char *)fdt_ptr + fdt_size) = cpu_to_fdt32(crc32);
-    BDK_TRACE(FDT_OS, "Exported device tree to memory %p, size 0x%x, CRC32 %08x\n",
-        fdt_ptr, fdt_size, crc32);
-    return bdk_ptr_to_phys(fdt_ptr);
-}
-
-/**
- * Return a pointer to the device tree used for configuration
- *
- * @return FDT or NULL on failure
- */
-void* bdk_config_get_fdt(void)
-{
-    return config_fdt;
-}
-
-/**
- * Set the device tree used for configuration
- *
- * @param fdt    Device tree to use. Memory is assumed to be from malloc() and bdk_config takes
- *               over ownership on success
- *
- * @return Zero on success, negative on failure
- */
-int bdk_config_set_fdt(void *fdt)
-{
-    int offset = fdt_path_offset(fdt, "/cavium,bdk"); /* Find our node */
-    if (offset < 0)
-        return -1;
-    free(config_fdt);
-    config_fdt = fdt;
-    config_node = offset;
-    return 0;
-}
-
-/**
- * Write all default values to a FDT. Missing config items get defaults in the
- * BDK config, this function adds those defaults to the FDT. This way other code
- * gets the default value without needing special code.
- *
- * @param fdt    FDT structure to fill defaults into
- *
- * @return Zero on success, negative on failure
- */
-int bdk_config_expand_defaults(void *fdt)
-{
-    const struct fdt_property *prop;
-
-    /* The best defaults may have changed while this image was running if DRAM
-       is setup. Update the defaults before expanding them */
-    config_set_defaults();
-
-    int fdt_node = fdt_path_offset(fdt, "/cavium,bdk"); /* Find our node */
-    if (fdt_node < 0)
-    {
-        bdk_error("Failed to find top node, FDT error %d: %s\n",
-            fdt_node, fdt_strerror(fdt_node));
-        return -1;
-    }
-
-    /* Loop through all configuration items */
-    for (bdk_config_t cfg = 0; cfg < __BDK_CONFIG_END; cfg++)
-    {
-        /* Figure out the base name without and dot parameters */
-        const char *name = config_info[cfg].format;
-        const char *name_end = strchr(name, '.');
-        int name_len;
-        if (name_end)
-            name_len = name_end - name;
-        else
-            name_len = strlen(name);
-        /* Try and find the base name in the FDT */
-        prop = fdt_get_property_namelen(fdt, fdt_node, name, name_len, NULL);
-        /* If it wasn't found, then we need to add the default */
-        if (prop == NULL)
-        {
-            /* Create a copy of the name for use in FDT calls */
-            char temp_name[name_len + 1];
-            memcpy(temp_name, name, name_len);
-            temp_name[name_len] = 0;
-            /* Call the correct FDT call based on the type */
-            int status = 0;
-            switch (config_info[cfg].ctype)
-            {
-                case BDK_CONFIG_TYPE_INT:
-                {
-                    char temp_value[20];
-                    if (config_info[cfg].default_value < 10)
-                        snprintf(temp_value, sizeof(temp_value), "%ld", config_info[cfg].default_value);
-                    else
-                        snprintf(temp_value, sizeof(temp_value), "0x%lx", config_info[cfg].default_value);
-                    /* Store the default int value */
-                    status = fdt_setprop_string(fdt, fdt_node, temp_name, temp_value);
-                    break;
-                }
-                case BDK_CONFIG_TYPE_STR:
-                    /* Store the default string value, if present */
-                    if (config_info[cfg].default_value)
-                    {
-                        status = fdt_setprop_string(fdt, fdt_node, temp_name,
-                            (const char *)config_info[cfg].default_value);
-                    }
-                    break;
-                case BDK_CONFIG_TYPE_STR_LIST:
-                    /* Do nothing, string list default to empty */
-                    break;
-                case BDK_CONFIG_TYPE_BINARY:
-                    /* Do nothing, binary defaults to empty */
-                    break;
-            }
-            if (status < 0)
-            {
-                bdk_error("Failed to set default for %s, FDT error %d: %s\n",
-                    temp_name, status, fdt_strerror(status));
-                return -1;
-            }
-        }
-    }
-    return 0;
-}
-
-/**
- * Some of the default config values can vary based on runtime parameters. This
- * function sets those default parameters. It must be run before anyone calls
- * bdk_config_get_*().
- */
-static void config_set_defaults(void)
-{
-    bool isEmulation = bdk_is_platform(BDK_PLATFORM_EMULATOR);
-    /* This is Cavium's OUI with the local admin bit. We will use this as a
-        default as it won't collide with official addresses, but is sort of
-        part of the Cavium range. The lower three bytes will be updated with
-        the wafer info */
-    uint64_t mac_address = 0x020fb7000000ull;
-    /* Set the lower MAC address bits based on the chip manufacturing
-        information. This should give reasonable MAC address defaults
-        for production parts */
-    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
-    {
-        BDK_CSR_INIT(fus_dat0, bdk_numa_local(), BDK_MIO_FUS_DAT0);
-        mac_address |= fus_dat0.u & 0xffffff;
-    }
-    else
-    {
-        mac_address |= bdk_fuse_read_range(bdk_numa_local(), BDK_FUS_FUSE_NUM_E_MFG_INFOX(0), 24);
-    }
-    config_info[BDK_CONFIG_MAC_ADDRESS].default_value = mac_address;
-
-    /* Set the number of packet buffers */
-    int num_packet_buffers = 4096;
-    /* If DRAM is setup, allocate 8K buffers for 8 ports plus some slop */
-    if (__bdk_is_dram_enabled(bdk_numa_master()))
-        num_packet_buffers = 8192 * 16 + 1024;
-    else if (isEmulation) {
-        if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
-            num_packet_buffers = 4096 * 4;
-    }
-    config_info[BDK_CONFIG_NUM_PACKET_BUFFERS].default_value = num_packet_buffers;
-    config_info[BDK_CONFIG_PACKET_BUFFER_SIZE].default_value = 1024;
-
-    /* Asim doesn't scale to 48 cores well. Limit to 4 */
-    if (bdk_is_platform(BDK_PLATFORM_ASIM))
-        config_info[BDK_CONFIG_COREMASK].default_value = 0xf;
-    /* CN88XX pass 1.x doesn't support EA */
-    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
-        config_info[BDK_CONFIG_PCIE_EA].default_value = 0;
-    /* Emulator only supports 4 cores */
-    if (isEmulation)
-        config_info[BDK_CONFIG_COREMASK].default_value = 0xf;
-}
-
-/**
- * BDK configuration items are stored in a device tree so thay can be passed to
- * other software later. This function creates the initial empty device tree
- * used for BDK configuration items. The values will be populated as configuration
- * files are read from flash.
- */
-static void config_setup_fdt(void)
-{
-    const int FDT_SIZE = 0x10000;
-    config_fdt = calloc(1, FDT_SIZE);
-    if (!config_fdt)
-        bdk_fatal("Unable to allocate memory for config FDT\n");
-    if (fdt_create_empty_tree(config_fdt, FDT_SIZE) < 0)
-        bdk_fatal("Unable to create FDT for config\n");
-    config_node = fdt_add_subnode(config_fdt, 0, "cavium,bdk");
-    if (config_node < 0)
-        bdk_fatal("Unable to create cavium,bdk node in FDT\n");
-}
-
-/**
- * Parse a FDT and copy its properties to our configuration FDT
- *
- * @param fdt    FDT to parse
- */
-static int config_parse_fdt(const void *fdt, const char *base_path)
-{
-    /* Check the FDT header */
-    int result = fdt_check_header(fdt);
-    if (result)
-        goto fail;
-
-    /* Find our node */
-    result = fdt_path_offset(fdt, base_path);
-    if (result < 0)
-        goto fail;
-
-    /* Copy all parameters to our in memory FDT */
-    int offset = fdt_first_property_offset(fdt, result);
-    while (offset >= 0)
-    {
-        const char *name = NULL;
-        int blob_size = 0;
-        const char *data = fdt_getprop_by_offset(fdt, offset, &name, &blob_size);
-        result = fdt_setprop(config_fdt, config_node, name, data, blob_size);
-        offset = fdt_next_property_offset(fdt, offset);
-    }
-    return 0;
-fail:
-    bdk_error("FDT error %d: %s\n", result, fdt_strerror(result));
-    return -1;
-}
-
-/**
- * Load a FDT from a file and pull in its configuration properties
- *
- * @param filename File to read from
- * @param offset   Offset into the file to read from
- *
- * @return Zero on success, negative on failure
- */
-static int config_load_file(const char *filename, uint64_t offset)
-{
-    uint64_t ftd_size = 0;
-    bdk_signed_flags_t sign_flags = BDK_SIGNED_FLAG_NONE;
-    if (offset)
-        sign_flags = BDK_SIGNED_FLAG_ALLOW_UNSIGNED | BDK_SIGNED_FLAG_NOT_ENCRYPTED;
-    void *fdt = bdk_signed_load(filename, offset, BDK_SIGNED_DTS, sign_flags, &ftd_size);
-    if (!fdt)
-        return -1;
-
-    /* Make sure the read succeeded */
-    if (ftd_size < (int)sizeof(struct fdt_header))
-    {
-        bdk_error("Invalid device tee %s\n", filename);
-        free(fdt);
-        return -1;
-    }
-
-    if (fdt_check_header(fdt))
-    {
-        bdk_error("Invalid FDT header read from %s\n", filename);
-        free(fdt);
-        return -1;
-    }
-
-    /* Make sure we read enough data to contain the FDT */
-    int correct_size = fdt_totalsize(fdt);
-    if ((int)ftd_size < correct_size)
-    {
-        bdk_error("Unable to read FDT from %s\n", filename);
-        free(fdt);
-        return -1;
-    }
-
-    /* Check if a CRC32 was added on the end of the FDT */
-    if ((int)ftd_size >= correct_size + 4)
-    {
-        uint32_t crc32 = bdk_crc32(fdt, correct_size, 0);
-        uint32_t correct_crc32 = *(uint32_t *)((const char *)fdt + correct_size);
-        /* CRC32 is stored in same endianness as FDT */
-        correct_crc32 = fdt32_to_cpu(correct_crc32);
-        if (crc32 != correct_crc32)
-        {
-            bdk_error("FDT failed CRC32 verification (%s)\n", filename);
-            free(fdt);
-            return -1;
-        }
-        //printf("PASS: FDT CRC32 verification (%s)\n", filename);
-    }
-
-    /* Parse the device tree, adding its configuration to ours */
-    if (config_parse_fdt(fdt, "/cavium,bdk"))
-    {
-        free(fdt);
-        return -1;
-    }
-
-    free(fdt);
-    return 0;
-}
-
-/**
- * Internal BDK function to initialize the config system. Must be called before
- * any configuration functions are called
- */
-void __bdk_config_init(void)
-{
-    bool done_trust_init = false;
-    /* Set default that can vary dynamically at runtime */
-    config_set_defaults();
-
-    /* Regsiter X1 is expected to be a device tree when we boot. Check that
-       the physical address seems correct, then load the device tree */
-    if ((__bdk_init_reg_x1 > 0) &&          /* Not zero */
-        (__bdk_init_reg_x1 < 0x1000000) &&   /* In the lower 16MB */
-        ((__bdk_init_reg_x1 & 0xfff) == 0)) /* Aligned on a 4KB boundary */
-    {
-        const void *fdt = (const void *)__bdk_init_reg_x1;
-        /* Check the FDT header */
-        int result = fdt_check_header(fdt);
-        if (result)
-            result = -1; /* Invalid tree */
-        else
-        {
-            int fdt_size = fdt_totalsize(fdt);
-            uint32_t crc32 = bdk_crc32(fdt, fdt_size, 0);
-            uint32_t correct_crc32 = *(uint32_t *)((const char *)fdt + fdt_size);
-            /* CRC32 is stored in same endianness as FDT */
-            correct_crc32 = fdt32_to_cpu(correct_crc32);
-            if (crc32 == correct_crc32)
-            {
-                //printf("Previous image FDT passed CRC32 verification(%p, size 0x%x, CRC32 %08x)\n", fdt, fdt_size, crc32);
-                result = fdt_path_offset(fdt, "/cavium,bdk"); /* Find our node */
-            }
-            else
-            {
-                bdk_error("Previous image FDT failed CRC32 verification(%p, size 0x%x)\n", fdt, fdt_size);
-                result = -1; /* Invalid tree */
-            }
-        }
-        /* If tree is valid so far, attempt to move it into our memory space */
-        if (result > 0)
-        {
-            /* 4KB extra room for growth */
-            const int fdt_size = fdt_totalsize(fdt) + 4096;
-            config_fdt = calloc(1, fdt_size);
-            if (config_fdt)
-            {
-                int result = fdt_move(fdt, config_fdt, fdt_size);
-                if (result == 0)
-                {
-                    /* Find our node */
-                    config_node = fdt_path_offset(config_fdt, "/cavium,bdk");
-                    if (config_node > 0)
-                    {
-                        printf("Using configuration from previous image\n");
-                        goto done;
-                    }
-                    else
-                    {
-                        bdk_error("Unable to find BDK node after move\n");
-                        free(config_fdt);
-                        config_node = 0;
-                        config_fdt = NULL;
-                    }
-                }
-                else
-                {
-                    bdk_error("Unable to move passed device tree\n");
-                    free(config_fdt);
-                    config_fdt = NULL;
-                }
-            }
-            else
-                bdk_error("Failed to allocate memory for passed device tree (%d bytes)\n", fdt_size);
-        }
-    }
-
-    /* Create the global device tree used to store config items */
-    config_setup_fdt();
-    /* Setup trust level so reading device trees works */
-    __bdk_trust_init();
-    done_trust_init = true;
-
-    if (bdk_is_platform(BDK_PLATFORM_ASIM))
-    {
-        if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
-            bdk_config_set_str("ASIM-CN88XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
-            bdk_config_set_str("ASIM-CN83XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
-            bdk_config_set_str("ASIM-CN81XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN93XX))
-            bdk_config_set_str("ASIM-CN93XX", BDK_CONFIG_BOARD_MODEL);
-    }
-    else if (bdk_is_platform(BDK_PLATFORM_EMULATOR))
-    {
-        if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
-            bdk_config_set_str("EMUL-CN88XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
-            bdk_config_set_str("EMUL-CN83XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
-            bdk_config_set_str("EMUL-CN81XX", BDK_CONFIG_BOARD_MODEL);
-        else if (CAVIUM_IS_MODEL(CAVIUM_CN93XX))
-            bdk_config_set_str("EMUL-CN93XX", BDK_CONFIG_BOARD_MODEL);
-    }
-    else if (config_load_file("/rom/boardcfg.dtb", 0) == 0)
-    {
-        printf("Board manufacturing information loaded from ROM-FS\n");
-    }
-    /* Load manufacturing data from the top 64KB of flash */
-    else if (config_load_file("/boot", BDK_CONFIG_MANUFACTURING_ADDRESS) != 0)
-    {
-        printf("\33[1m"); /* Bold */
-        bdk_warn("\n");
-        bdk_warn("********************************************************\n");
-        bdk_warn("* Board manufacturing information not found.  Program\n");
-        bdk_warn("* the board manufacturing information in the Setup menu.\n");
-        bdk_warn("********************************************************\n");
-        bdk_warn("\n");
-        printf("\33[0m"); /* Normal */
-        goto done;
-    }
-
-    const char *model = bdk_config_get_str(BDK_CONFIG_BOARD_MODEL);
-    const char *revision = bdk_config_get_str(BDK_CONFIG_BOARD_REVISION);
-
-    /* Load BOARD-REVISION.cfg if it is on ROM-FS */
-    if (model && revision)
-    {
-        char filename[64];
-        snprintf(filename, sizeof(filename), "/rom/%s-%s.dtb", model, revision);
-        if (config_load_file(filename, 0) == 0)
-            goto done;
-    }
-
-    /* Load BOARD.cfg if it is on ROM-FS */
-    if (model)
-    {
-        char filename[64];
-        snprintf(filename, sizeof(filename), "/rom/%s.dtb", model);
-        if (config_load_file(filename, 0) == 0)
-            goto done;
-    }
-
-    /* Load default.dtb if it is there */
-    if (config_load_file("/fatfs/default.dtb", 0) == 0)
-        goto done;
-
-    /* Load BOARD-REVISION.cfg if it is there */
-    if (model && revision)
-    {
-        char filename[64];
-        snprintf(filename, sizeof(filename), "/fatfs/%s-%s.dtb", model, revision);
-        if (config_load_file(filename, 0) == 0)
-            goto done;
-    }
-
-    /* Load BOARD.cfg if it is there */
-    if (model)
-    {
-        char filename[64];
-        snprintf(filename, sizeof(filename), "/fatfs/%s.dtb", model);
-        if (config_load_file(filename, 0) == 0)
-            goto done;
-    }
-
-    /* No board specific configuration was found. Warn the user */
-    printf("\33[1m"); /* Bold */
-    bdk_warn("\n");
-    bdk_warn("********************************************************\n");
-    bdk_warn("* Board configuration file not found. Either the board\n");
-    bdk_warn("* model is incorrect, or factory settings are not\n");
-    bdk_warn("* available. DTB file not found for board \"%s\".\n", model);
-    bdk_warn("********************************************************\n");
-    bdk_warn("\n");
-    printf("\33[0m"); /* Normal */
-
-done:
-    bdk_config_set_str(bdk_version_string(), BDK_CONFIG_VERSION);
-    /* Load the tracing level */
-    bdk_trace_enables = bdk_config_get_int(BDK_CONFIG_TRACE);
-    if (BDK_TRACE_OVERRIDE)
-        bdk_trace_enables = BDK_TRACE_OVERRIDE;
-    if (!done_trust_init)
-        __bdk_trust_init();
-}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c
new file mode 100644
index 0000000..8120820
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c
@@ -0,0 +1,373 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include "libbdk-arch/bdk-csrs-ecam.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-pccpf.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-ecam.h"
+
+#if 1 /* Support CN88XX pass 1.0 */
+/*******************************************************************
+ *******************************************************************
+    These functions are related to CN88XX pass 1.0 errata and do not
+    apply to any other chip
+ *******************************************************************
+ *******************************************************************/
+
+/**
+ * Errata (ECAM-22630) ECAM function accesses can fault
+ * For some errata workaround we need a check to tell if a ECAM access is to a
+ * valid intenral device. This function decodes a pcc_dev_con_e enumeration and
+ * checks if the supplied arguments match it. This should only
+ * ever be called on CN88XX pass 1.0.
+ *
+ * @param ecam    ECAM to check
+ * @param bus     ECAM bus number
+ * @param dev     Device to check
+ * @param fn      sub function of device
+ * @param dev_con Enumeration to match against
+ *
+ * @return Non zero if the device matches
+ */
+static int is_internal_cn88xxp1_0(const bdk_device_t *device, int dev_con)
+{
+    union bdk_pcc_dev_con_s d = { .u = dev_con };
+    return (d.cn8.ecam == device->ecam) && (d.s.bus == device->bus) && (d.s.func == ((device->dev<<3)|device->func));
+}
+
+/**
+ * Errata (ECAM-22630) ECAM function accesses can fault
+ * This is a companion to the function above to determine if the ECAM device is
+ * any of the valid internal devices. This should only ever be
+ * called on CN88XX pass 1.0.
+ *
+ * @param ecam   ECAM to check
+ * @param bus    ECAM bus number
+ * @param dev    Device to check
+ * @param fn     sub function of device
+ *
+ * @return Non zero if the device matches
+ */
+static int is_any_internal_cn88xxp1_0(const bdk_device_t *device)
+{
+    /* Errata (ECAM-22630) ECAM function accesses can fault
+        CN88XXP1.0: The ECAM has a bug where accessing a non-existent
+        device causes an exception. This is a list of all valid devices
+        for CN88XX pass 1.0 */
+    static const uint32_t INTERNAL_DEVICES_CN88XXP1_0[] = {
+        BDK_PCC_DEV_CON_E_BGXX(0),
+        BDK_PCC_DEV_CON_E_BGXX(1),
+        BDK_PCC_DEV_CON_E_DAP,
+        BDK_PCC_DEV_CON_E_DFA,
+        BDK_PCC_DEV_CON_E_FUSF,
+        BDK_PCC_DEV_CON_E_GIC_CN8,
+        BDK_PCC_DEV_CON_E_GPIO_CN8,
+        BDK_PCC_DEV_CON_E_GSERX(0),
+        BDK_PCC_DEV_CON_E_GSERX(1),
+        BDK_PCC_DEV_CON_E_GSERX(10),
+        BDK_PCC_DEV_CON_E_GSERX(11),
+        BDK_PCC_DEV_CON_E_GSERX(12),
+        BDK_PCC_DEV_CON_E_GSERX(13),
+        BDK_PCC_DEV_CON_E_GSERX(2),
+        BDK_PCC_DEV_CON_E_GSERX(3),
+        BDK_PCC_DEV_CON_E_GSERX(4),
+        BDK_PCC_DEV_CON_E_GSERX(5),
+        BDK_PCC_DEV_CON_E_GSERX(6),
+        BDK_PCC_DEV_CON_E_GSERX(7),
+        BDK_PCC_DEV_CON_E_GSERX(8),
+        BDK_PCC_DEV_CON_E_GSERX(9),
+        BDK_PCC_DEV_CON_E_GTI_CN8,
+        BDK_PCC_DEV_CON_E_IOBNX(0),
+        BDK_PCC_DEV_CON_E_IOBNX(1),
+        BDK_PCC_DEV_CON_E_KEY,
+        BDK_PCC_DEV_CON_E_L2C,
+        BDK_PCC_DEV_CON_E_L2C_CBCX(0),
+        BDK_PCC_DEV_CON_E_L2C_CBCX(1),
+        BDK_PCC_DEV_CON_E_L2C_CBCX(2),
+        BDK_PCC_DEV_CON_E_L2C_CBCX(3),
+        BDK_PCC_DEV_CON_E_L2C_MCIX(0),
+        BDK_PCC_DEV_CON_E_L2C_MCIX(1),
+        BDK_PCC_DEV_CON_E_L2C_MCIX(2),
+        BDK_PCC_DEV_CON_E_L2C_MCIX(3),
+        BDK_PCC_DEV_CON_E_L2C_TADX(0),
+        BDK_PCC_DEV_CON_E_L2C_TADX(1),
+        BDK_PCC_DEV_CON_E_L2C_TADX(2),
+        BDK_PCC_DEV_CON_E_L2C_TADX(3),
+        BDK_PCC_DEV_CON_E_L2C_TADX(4),
+        BDK_PCC_DEV_CON_E_L2C_TADX(5),
+        BDK_PCC_DEV_CON_E_L2C_TADX(6),
+        BDK_PCC_DEV_CON_E_L2C_TADX(7),
+        BDK_PCC_DEV_CON_E_LMCX(0),
+        BDK_PCC_DEV_CON_E_LMCX(1),
+        BDK_PCC_DEV_CON_E_LMCX(2),
+        BDK_PCC_DEV_CON_E_LMCX(3),
+        BDK_PCC_DEV_CON_E_MIO_BOOT,
+        BDK_PCC_DEV_CON_E_MIO_EMM,
+        BDK_PCC_DEV_CON_E_MIO_FUS,
+        BDK_PCC_DEV_CON_E_MIO_PTP,
+        BDK_PCC_DEV_CON_E_MIO_TWSX(0),
+        BDK_PCC_DEV_CON_E_MIO_TWSX(1),
+        BDK_PCC_DEV_CON_E_MIO_TWSX(2),
+        BDK_PCC_DEV_CON_E_MIO_TWSX(3),
+        BDK_PCC_DEV_CON_E_MIO_TWSX(4),
+        BDK_PCC_DEV_CON_E_MIO_TWSX(5),
+        BDK_PCC_DEV_CON_E_MPI,
+        BDK_PCC_DEV_CON_E_MRML,
+        BDK_PCC_DEV_CON_E_NCSI,
+        BDK_PCC_DEV_CON_E_NIC_CN88XX,
+        BDK_PCC_DEV_CON_E_OCLAX_CN8(0),
+        BDK_PCC_DEV_CON_E_OCLAX_CN8(1),
+        BDK_PCC_DEV_CON_E_OCLAX_CN8(2),
+        BDK_PCC_DEV_CON_E_OCLAX_CN8(3),
+        BDK_PCC_DEV_CON_E_OCLAX_CN8(4),
+        BDK_PCC_DEV_CON_E_OCX,
+        BDK_PCC_DEV_CON_E_PCCBR_DFA,
+        BDK_PCC_DEV_CON_E_PCCBR_MRML,
+        BDK_PCC_DEV_CON_E_PCCBR_NIC_CN88XX,
+        BDK_PCC_DEV_CON_E_PCCBR_RAD_CN88XX,
+        BDK_PCC_DEV_CON_E_PCCBR_ZIP_CN88XX,
+        BDK_PCC_DEV_CON_E_PCIERC0_CN88XX,
+        BDK_PCC_DEV_CON_E_PCIERC1_CN88XX,
+        BDK_PCC_DEV_CON_E_PCIERC2_CN88XX,
+        BDK_PCC_DEV_CON_E_PCIERC3_CN88XX,
+        BDK_PCC_DEV_CON_E_PCIERC4,
+        BDK_PCC_DEV_CON_E_PCIERC5,
+        BDK_PCC_DEV_CON_E_PEMX(0),
+        BDK_PCC_DEV_CON_E_PEMX(1),
+        BDK_PCC_DEV_CON_E_PEMX(2),
+        BDK_PCC_DEV_CON_E_PEMX(3),
+        BDK_PCC_DEV_CON_E_PEMX(4),
+        BDK_PCC_DEV_CON_E_PEMX(5),
+        BDK_PCC_DEV_CON_E_RAD_CN88XX,
+        BDK_PCC_DEV_CON_E_RNM_CN88XX,
+        BDK_PCC_DEV_CON_E_RST,
+        BDK_PCC_DEV_CON_E_SATA0_CN88XX,
+        BDK_PCC_DEV_CON_E_SATA1_CN88XX,
+        BDK_PCC_DEV_CON_E_SATA10,
+        BDK_PCC_DEV_CON_E_SATA11,
+        BDK_PCC_DEV_CON_E_SATA12,
+        BDK_PCC_DEV_CON_E_SATA13,
+        BDK_PCC_DEV_CON_E_SATA14,
+        BDK_PCC_DEV_CON_E_SATA15,
+        BDK_PCC_DEV_CON_E_SATA2,
+        BDK_PCC_DEV_CON_E_SATA3,
+        BDK_PCC_DEV_CON_E_SATA4,
+        BDK_PCC_DEV_CON_E_SATA5,
+        BDK_PCC_DEV_CON_E_SATA6,
+        BDK_PCC_DEV_CON_E_SATA7,
+        BDK_PCC_DEV_CON_E_SATA8,
+        BDK_PCC_DEV_CON_E_SATA9,
+        BDK_PCC_DEV_CON_E_SGP,
+        BDK_PCC_DEV_CON_E_SLI0_CN88XX,
+        BDK_PCC_DEV_CON_E_SLI1,
+        BDK_PCC_DEV_CON_E_SMI,
+        BDK_PCC_DEV_CON_E_SMMU0_CN8,
+        BDK_PCC_DEV_CON_E_SMMU1,
+        BDK_PCC_DEV_CON_E_SMMU2,
+        BDK_PCC_DEV_CON_E_SMMU3,
+        BDK_PCC_DEV_CON_E_TNS,
+        BDK_PCC_DEV_CON_E_UAAX_CN8(0),
+        BDK_PCC_DEV_CON_E_UAAX_CN8(1),
+        BDK_PCC_DEV_CON_E_USBHX(0),
+        BDK_PCC_DEV_CON_E_USBHX(1),
+        BDK_PCC_DEV_CON_E_VRMX(0),
+        BDK_PCC_DEV_CON_E_VRMX(1),
+        BDK_PCC_DEV_CON_E_ZIP_CN88XX,
+        0,
+    };
+
+    int loc = 0;
+    while (INTERNAL_DEVICES_CN88XXP1_0[loc])
+    {
+        if (is_internal_cn88xxp1_0(device, INTERNAL_DEVICES_CN88XXP1_0[loc]))
+            return 1;
+        loc++;
+    }
+    return 0;
+}
+
+static int is_accessable_cn88xxp1_0(const bdk_device_t *device)
+{
+    /* Errata (ECAM-22630) ECAM function accesses can fault */
+    /* Skip internal devices that don't exists */
+    if (!is_any_internal_cn88xxp1_0(device))
+        return 0;
+
+    /* Errata (ECAM-23020) PCIERC transactions fault unless PEM is
+       out of reset. The PCIe ports don't work until the PEM is
+       turned on. Check for one of the PCIe ports */
+    int pem = -1;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC0_CN88XX))
+        pem = 0;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC1_CN88XX))
+        pem = 1;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC2_CN88XX))
+        pem = 2;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC3_CN88XX))
+        pem = 3;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC4))
+        pem = 4;
+    if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC5))
+        pem = 5;
+    if (pem != -1)
+    {
+        BDK_CSR_INIT(pem_on, device->node, BDK_PEMX_ON(pem));
+        if (!pem_on.s.pemon || !pem_on.s.pemoor)
+            return 0;
+    }
+
+    {
+        /* SATA ports should be hidden if they aren't configured at the QLM */
+        int qlm = -1;
+        if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA0_CN88XX) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA1_CN88XX) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA2) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA3))
+            qlm = 2;
+        if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA4) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA5) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA6) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA7))
+            qlm = 3;
+        if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA8) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA9) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA10) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA11))
+            qlm = 6;
+        if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA12) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA13) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA14) ||
+            is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA15))
+            qlm = 7;
+        if (qlm != -1)
+        {
+            BDK_CSR_INIT(cfg, device->node, BDK_GSERX_CFG(qlm));
+            if (!cfg.s.sata)
+                return 0;
+        }
+    }
+    return 1;
+}
+
+#endif /* Support CN88XX pass 1.0 */
+
+/**
+ * Build an ECAM config space request address for a device
+ *
+ * @param device Device being accessed
+ * @param reg    Register to access
+ *
+ * @return 64bit IO address
+ */
+uint64_t __bdk_ecam_build_address(const bdk_device_t *device, int reg)
+{
+    /* CN88XX pass 1.0 had a plethora of errata related to ECAM access. This
+       checks to make sure we're allowed to access this location based on
+       the various errata */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_0) && !is_accessable_cn88xxp1_0(device))
+        return 0;
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+    {
+        /* Build the address */
+        union bdk_ecam_cfg_addr_s address;
+        address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(device->ecam);
+        address.s.node = device->node;
+        address.s.bus = device->bus;
+        address.s.func = device->dev << 3 | device->func;
+        address.s.addr = reg;
+        return address.u;
+    }
+    else
+    {
+        /* Build the address. The architects decided to make it different
+           from CN8XXX for no obvious reason */
+        union bdk_ecam_cfg_addr_s address;
+        address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(0);
+        address.s.node = device->node;
+        address.s.dmn = device->ecam;
+        address.s.bus = device->bus;
+        address.s.func = device->dev << 3 | device->func;
+        address.s.addr = reg;
+        return address.u;
+    }
+}
+
+/**
+ * Read from an ECAM
+ *
+ * @param device Device to read from
+ * @param reg    Register to read
+ *
+ * @return Result of the read of -1 on failure
+ */
+uint32_t bdk_ecam_read32(const bdk_device_t *device, int reg)
+{
+    uint64_t address = __bdk_ecam_build_address(device, reg);
+    uint32_t result;
+    if (address)
+        result = bdk_le32_to_cpu(bdk_read64_uint32(address));
+    else
+        result = 0xffffffff;
+
+    /* Errata ECAM-22630: CN88XX pass 1.x, except pass 1.0, will return zero
+       for non-existent devices instead of ones. We look for this special case
+       for 32bit reads for reg=0 so we can scan device properly */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X) && (reg == 0) && (result == 0))
+        result = 0xffffffff;
+
+    return result;
+}
+
+/**
+ * Write to an ECAM register
+ *
+ * @param device Device to write to
+ * @param reg    Register to write
+ * @param value  Value to write
+ */
+void bdk_ecam_write32(const bdk_device_t *device, int reg, uint32_t value)
+{
+    uint64_t address = __bdk_ecam_build_address(device, reg);
+    if (address)
+        bdk_write64_uint32(address, bdk_cpu_to_le32(value));
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam.c
new file mode 100644
index 0000000..f79eb58
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam.c
@@ -0,0 +1,216 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-arch/bdk-csrs-ecam.h"
+#include "libbdk-arch/bdk-csrs-pccbr.h"
+#include "libbdk-arch/bdk-csrs-pccpf.h"
+#include "libbdk-arch/bdk-csrs-rvu.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-ecam.h"
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(ECAM);
+
+/**
+ * Walk an ECAM finding all internal devices. Each internal
+ * device is then added to the list of device maintained by
+ * bdk-device.
+ *
+ * @param node   Node to walk
+ * @param ecam   Ecam to walk
+ * @param bus    Zero on first call. Will be non-zero when sub busses are walked
+ */
+static void ecam_walk_internal_bus(bdk_node_t node, int ecam, int bus)
+{
+    /* Create a fake bdk-device to pass around until we create the
+       real device */
+    bdk_device_t device;
+    memset(&device, 0, sizeof(device));
+    device.node = node;
+    device.ecam = ecam;
+    device.bus = bus;
+
+    /* Scan all possible device IDs on the bus */
+    for (int dev = 0; dev < 32; dev++)
+    {
+        /* Update the current scan location */
+        device.dev = dev;
+        device.func = 0;
+
+        uint32_t device_id = bdk_ecam_read32(&device, BDK_PCCPF_XXX_ID);
+
+        /* Only add devices that exist. Our internal devices can have function
+           zero missing. The all ones we get back matches the multi-function
+           check, but not a bridge. This means the later code works fine */
+        if (device_id != (uint32_t)-1)
+            bdk_device_add(device.node, device.ecam, device.bus, device.dev, device.func);
+
+        /* Check for Multi function and Bridge devices */
+        BDK_CSR_DEFINE(clsize, BDK_PCCPF_XXX_CLSIZE);
+        clsize.u = bdk_ecam_read32(&device, BDK_PCCPF_XXX_CLSIZE);
+        int ismultifunction = (clsize.s.hdrtype & 0x80);
+        int isbridge = (clsize.s.hdrtype & 0x7f) == 1;
+
+        if (ismultifunction)
+        {
+            /* Scan for other functions on multifunction devices */
+            for (int func = 1; func < 8; func++)
+            {
+                /* Check if we're past all functions */
+                device.func = func;
+                device_id = bdk_ecam_read32(&device, BDK_PCCPF_XXX_ID);
+                if (device_id != (uint32_t)-1)
+                    bdk_device_add(device.node, device.ecam, device.bus, device.dev, device.func);
+            }
+            device.func = 0;
+        }
+        if (isbridge)
+        {
+            /* Internal bus numbers are hard coded. Read the bus ID */
+            bdk_pccbr_xxx_bus_t ibus;
+            ibus.u = bdk_ecam_read32(&device, BDK_PCCBR_XXX_BUS);
+            /* Asim used to have a bug where bus number were zero, report errors
+               for those */
+            if (ibus.s.sbnum == 0)
+            {
+                bdk_error("N%d:E%d:%d:%d.%d: Secondary bus number is zero\n",
+                    device.node, device.ecam, device.bus, device.dev, device.func);
+            }
+            /* Real PCIe external device use high bus numbers, so skip them */
+            else if (ibus.s.sbnum < 16)
+            {
+                ecam_walk_internal_bus(node, ecam, ibus.s.sbnum);
+            }
+        }
+    }
+}
+
+/**
+ * Return the number of internal ECAMS on a node.
+ *
+ * @param node   Node to query
+ *
+ * @return Number of ECAMs available
+ */
+int bdk_ecam_get_num(bdk_node_t node)
+{
+    /* CN88XX lacks the ECAM_CONST for finding the number of ECAMs */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+        return 4;
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN93XX))
+        return 3; /* Map ECAMs to the first 3 domains */
+    else
+    {
+        BDK_CSR_INIT(ecam_const, node, BDK_ECAMX_CONST(0));
+        if (ecam_const.s.ecams == 0)
+        {
+            bdk_error("N%d.ECAM: Number of ecams incorrect in ECAMX_CONST\n", node);
+            return 1;
+        }
+        return ecam_const.s.ecams;
+    }
+}
+
+/**
+ * Initialize RVU functions for use by the BDK. This doesn't setup the hardware
+ * behind RVU, juse allows register access to it. The BDK uses a static RVU
+ * configuration where everything is accessable from RVU PF0.
+ *
+ * @param node   Node to initialize
+ *
+ * @return Zero on success, negative on failure
+ */
+static int __bdk_ecam_rvu_init(bdk_node_t node)
+{
+    const int rvu_pf = 0;
+    /* Enable PF access to all blocks */
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_CPTX_CFG(rvu_pf, 0),
+        c.s.num_lfs = 1); // FIXME: How many LFs?
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_INT_CFG(rvu_pf),
+        c.s.msix_offset = 0);
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_MSIX_CFG(rvu_pf),
+        c.s.pf_msixt_offset = 0;
+        c.s.pf_msixt_sizem1 = 0;
+        c.s.vf_msixt_offset = 0;
+        c.s.vf_msixt_sizem1 = 0);
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_NIXX_CFG(rvu_pf, 0),
+        c.s.has_lf = 1);
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_NPA_CFG(rvu_pf),
+        c.s.has_lf = 1);
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_SSO_CFG(rvu_pf),
+        c.s.num_lfs = 1); // FIXME: How many LFs?
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_SSOW_CFG(rvu_pf),
+        c.s.num_lfs = 1); // FIXME: How many LFs?
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_TIM_CFG(rvu_pf),
+        c.s.num_lfs = 1); // FIXME: How many LFs?
+    /* Enable RVU with full access */
+    BDK_CSR_MODIFY(c, node, BDK_RVU_PRIV_PFX_CFG(rvu_pf),
+        c.s.me_flr_ena = 1;
+        c.s.af_ena = 1;
+        c.s.ena = 1;
+        c.s.nvf = 0;
+        c.s.first_hwvf = 0);
+    return 0;
+}
+
+/**
+ * Scan all ECAMs for devices and add them to bdk-device
+ *
+ * @param node   Node to scan
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_ecam_scan_all(bdk_node_t node)
+{
+    /* RVU must be setup before we scan the bus otherwise it doesn't
+       show up */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN9XXX))
+        __bdk_ecam_rvu_init(node);
+
+    int num_ecams = bdk_ecam_get_num(node);
+    for (int ecam = 0; ecam < num_ecams; ecam++)
+        ecam_walk_internal_bus(node, ecam, 0);
+
+    bdk_device_init();
+
+    return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-gpio.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-gpio.c
index 55f0dbf..986f68f 100644
--- a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-gpio.c
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-gpio.c
@@ -38,6 +38,7 @@
 ***********************license end**************************************/
 #include <bdk.h>
 #include "libbdk-arch/bdk-csrs-gpio.h"
+#include "libbdk-hal/bdk-gpio.h"
 
 /* This code is an optional part of the BDK. It is only linked in
     if BDK_REQUIRE() needs it */
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-l2c.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-l2c.c
index b1e2a88..6c163da 100644
--- a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-l2c.c
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-l2c.c
@@ -41,6 +41,8 @@
 #include "libbdk-arch/bdk-csrs-l2c.h"
 #include "libbdk-arch/bdk-csrs-l2c_cbc.h"
 #include "libbdk-arch/bdk-csrs-mio_fus.h"
+#include "libbdk-hal/bdk-l2c.h"
+#include "libbdk-hal/bdk-utils.h"
 
 typedef struct
 {
@@ -51,56 +53,6 @@
 
 static l2_node_state_t l2_node_state[BDK_NUMA_MAX_NODES];
 
-/**
- * Perform one time initialization of L2 for improved
- * performance. This can be called after L2 is in use.
- *
- * @return Zero on success, negative on failure.
- */
-int bdk_l2c_initialize(bdk_node_t node)
-{
-    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
-    {
-        /* Tell L2 to give the IOB statically higher priority compared to the
-            cores. This avoids conditions where IO blocks might be starved under
-            very high L2 loads */
-        BDK_CSR_MODIFY(c, node, BDK_L2C_CTL,
-            c.s.rsp_arb_mode = 1;
-            c.s.xmc_arb_mode = 0);
-    }
-
-    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X) && !bdk_is_platform(BDK_PLATFORM_ASIM))
-    {
-        /* Errata: (L2C-22279) RCAS/RSTC which hits S/S can use wrong compare data */
-        BDK_CSR_MODIFY(c, node, BDK_L2C_CTL,
-            c.s.dissblkdty = 1);
-        /* Errata: (L2C-22249) Broadcast invals can cause starvation on the INV bus */
-        for (int i = 0; i < 4; i++)
-            BDK_CSR_MODIFY(c, node, BDK_L2C_CBCX_SCRATCH(i),
-                c.s.invdly = 1);
-    }
-
-    // FIXME: Disable partial writes on pass 2 until it is debugged
-    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS2_X) && !bdk_is_platform(BDK_PLATFORM_ASIM))
-    {
-        BDK_CSR_MODIFY(c, node, BDK_L2C_CTL,
-            c.s.dissblkdty = 1);
-    }
-
-    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX) && bdk_is_platform(BDK_PLATFORM_EMULATOR))
-    {
-        /* The emulator requires L2C_CTL[DISSBLKDTY] to be set */
-        BDK_CSR_MODIFY(c, node, BDK_L2C_CTL,
-            c.s.dissblkdty = 1);
-    }
-     return 0;
-}
-
-int bdk_l2c_get_core_way_partition(bdk_node_t node, int core)
-{
-    return (BDK_CSR_READ(node, BDK_L2C_WPAR_PPX(core)) & 0xffff);
-}
-
 int bdk_l2c_set_core_way_partition(bdk_node_t node, int core, uint32_t mask)
 {
     uint32_t valid_mask = (1 << bdk_l2c_get_num_assoc(node)) - 1;
@@ -120,82 +72,11 @@
     return 0;
 }
 
-
 int bdk_l2c_get_hw_way_partition(bdk_node_t node)
 {
     return (BDK_CSR_READ(node, BDK_L2C_WPAR_IOBX(0)) & 0xffff);
 }
 
-
-int bdk_l2c_lock_mem_region(bdk_node_t node, uint64_t start, uint64_t len)
-{
-    /* Round start/end to cache line boundaries */
-    len += start & BDK_CACHE_LINE_MASK;
-    start &= ~BDK_CACHE_LINE_MASK;
-    len = (len + BDK_CACHE_LINE_MASK) & ~BDK_CACHE_LINE_MASK;
-    void *ptr = (start) ? bdk_phys_to_ptr(start) : NULL;
-
-    while (len)
-    {
-        BDK_CACHE_LCK_L2(ptr);
-        ptr += BDK_CACHE_LINE_SIZE;
-        len -= BDK_CACHE_LINE_SIZE;
-    }
-    l2_node_state[node].is_locked = true;
-    return 0;
-}
-
-void bdk_l2c_flush(bdk_node_t node)
-{
-    /* The number of ways can be reduced with fuses, but the equations below
-       assume the max number of ways */
-    const int MAX_WAYS = 16;
-    int num_sets = bdk_l2c_get_num_sets(node);
-    int num_ways = bdk_l2c_get_num_assoc(node);
-
-    int is_rtg = 1; /* Clear remote tags */
-    for (int l2_way = 0; l2_way < num_ways; l2_way++)
-    {
-        for (int l2_set = 0; l2_set < num_sets; l2_set++)
-        {
-            uint64_t encoded = 128 * (l2_set + num_sets * (l2_way + (is_rtg * MAX_WAYS)));
-            BDK_CACHE_WBI_L2_INDEXED(encoded);
-        }
-    }
-
-    is_rtg = 0; /* Clear local tags */
-    for (int l2_way = 0; l2_way < num_ways; l2_way++)
-    {
-        for (int l2_set = 0; l2_set < num_sets; l2_set++)
-        {
-            uint64_t encoded = 128 * (l2_set + num_sets * (l2_way + (is_rtg * MAX_WAYS)));
-            BDK_CACHE_WBI_L2_INDEXED(encoded);
-        }
-    }
-    l2_node_state[node].is_locked = false;
-}
-
-int bdk_l2c_unlock_mem_region(bdk_node_t node, uint64_t start, uint64_t len)
-{
-    /* Round start/end to cache line boundaries */
-    len += start & BDK_CACHE_LINE_MASK;
-    start &= ~BDK_CACHE_LINE_MASK;
-    len = (len + BDK_CACHE_LINE_MASK) & ~BDK_CACHE_LINE_MASK;
-    void *ptr = (start) ? bdk_phys_to_ptr(start) : NULL;
-
-    while (len > 0)
-    {
-        /* Must use invalidate version to release lock */
-        BDK_CACHE_WBI_L2(ptr);
-        ptr += BDK_CACHE_LINE_SIZE;
-        len -= BDK_CACHE_LINE_SIZE;
-    }
-
-    l2_node_state[node].is_locked = false;
-    return 0;
-}
-
-
 int bdk_l2c_get_cache_size_bytes(bdk_node_t node)
 {
     return bdk_l2c_get_num_sets(node) * bdk_l2c_get_num_assoc(node) * BDK_CACHE_LINE_SIZE;
@@ -254,17 +135,22 @@
     return l2_node_state[node].ways;
 }
 
-/**
- * Return true if the BDK has locked itself in L2
- *
- * @return
- */
-int bdk_l2c_is_locked(bdk_node_t node)
+int bdk_l2c_unlock_mem_region(bdk_node_t node, uint64_t start, uint64_t len)
 {
-    /* Determining the lock state of L2 requires reading exact tags from L2
-       which varies per chip. Rather than deal with that complexity, we just
-       keep a flag around saying if the L2 lock functions have been called.
-       This works for the BDK as its use of locking is very simple */
-    return l2_node_state[node].is_locked;
-}
+    /* Round start/end to cache line boundaries */
+    len += start & BDK_CACHE_LINE_MASK;
+    start &= ~BDK_CACHE_LINE_MASK;
+    len = (len + BDK_CACHE_LINE_MASK) & ~BDK_CACHE_LINE_MASK;
+    void *ptr = (start) ? bdk_phys_to_ptr(start) : NULL;
 
+    while (len > 0)
+    {
+        /* Must use invalidate version to release lock */
+        BDK_CACHE_WBI_L2(ptr);
+        ptr += BDK_CACHE_LINE_SIZE;
+        len -= BDK_CACHE_LINE_SIZE;
+    }
+
+    l2_node_state[node].is_locked = false;
+    return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-nic.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-nic.c
new file mode 100644
index 0000000..b6a9384
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-nic.c
@@ -0,0 +1,1090 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <malloc.h>
+#include "libbdk-arch/bdk-csrs-nic.h"
+
+#define MAX_MTU 9212
+#define CQ_ENTRIES_QSIZE 0
+#define CQ_ENTRIES (1024 << CQ_ENTRIES_QSIZE)
+#define SQ_ENTRIES_QSIZE 0
+#define SQ_ENTRIES (1024 << SQ_ENTRIES_QSIZE)
+#define RBDR_ENTRIES_QSIZE 0
+#define RBDR_ENTRIES (8192 << RBDR_ENTRIES_QSIZE)
+
+typedef struct
+{
+    /* VNIC related config */
+    bdk_node_t  node : 8;       /* Node the NIC is on */
+    bdk_nic_type_t ntype : 8;   /* They type of device this NIC is connected to */
+    uint8_t     nic_vf;         /* NIC VF index number (0 - MAX_VNIC-1) */
+    uint8_t     sq;             /* Send Queue (SQ) inside NIC VF (0-7) */
+    uint8_t     cq;             /* Complete Queue (CQ) inside NIC VF (0-7) */
+    uint8_t     rq;             /* Receive Queue (RQ) inside NIC VF (0-7) */
+    uint8_t     rbdr;           /* Receive Buffer Descriptor Ring (RBDR) inside NIC VF (0-1) */
+    uint8_t     bpid;           /* Backpressure ID (0-127) */
+    bdk_if_handle_t handle;     /* bdk-if handle associated with this NIC */
+
+    /* Transmit */
+    void *      sq_base;        /* Pointer to the beginning of the SQ in memory */
+    int         sq_loc;         /* Location where the next send should go */
+    int         sq_available;   /* Amount of space left in the queue (fuzzy) */
+} nic_t;
+
+typedef struct
+{
+    void *base;
+    int loc;
+} nic_rbdr_state_t;
+
+typedef struct
+{
+    int num_nic_vf;
+    int next_free_nic_vf;
+    int next_free_cpi;
+    int next_free_rssi;
+    int next_free_bpid;
+    nic_t *nic_map[0]; /* Indexed by handle->nic_id */
+} nic_node_state_t;
+
+static nic_node_state_t *global_node_state[BDK_NUMA_MAX_NODES];
+static int global_buffer_size = 0;
+
+/**
+ * Setup a receive Completion Queue (CQ). CQ can be shared across multiple NICs
+ * to save space. This happens if the NIC has "shares_cq" set.
+ *
+ * @param nic    NIC to setup
+ *
+ * @return Zero on success, negative on failure
+ */
+static int vnic_setup_cq(nic_t *nic)
+{
+    /* CN88XX pass 1.x had the drop level reset value too low */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_CQM_CFG,
+        c.s.drop_level = 128);
+
+    /* All devices using the same NIC VF use the same CQ */
+    if (nic->handle->index == 0)
+    {
+        BDK_TRACE(NIC, "%s: Setting up CQ(%d, %d)\n", nic->handle->name, nic->nic_vf, nic->cq);
+        /* Note that the completion queue requires 512 byte alignment */
+        void *cq_memory = memalign(512, 512 * CQ_ENTRIES);
+        if (!cq_memory)
+        {
+            bdk_error("%s: Failed to allocate memory for completion queue\n", nic->handle->name);
+            return -1;
+        }
+        /* Configure the completion queue (CQ) */
+        BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_CQX_BASE(nic->nic_vf, nic->cq),
+            bdk_ptr_to_phys(cq_memory));
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_QSX_CQX_CFG(nic->nic_vf, nic->cq),
+            c.s.ena = 1;
+            c.s.caching = 1;
+            c.s.qsize = CQ_ENTRIES_QSIZE);
+    }
+
+    /* Configure our vnic to send to the CQ */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_SQX_CFG(nic->nic_vf, nic->sq),
+        c.s.cq_qs = nic->nic_vf;
+        c.s.cq_idx = nic->cq);
+    return 0;
+}
+
+/**
+ * Add buffers to a receive buffer descriptor ring (RBDR). Note that RBDRs are
+ * shared between NICs using the same CQ.
+ *
+ * @param nic       NIC using the RBDR
+ * @param rbdr_free Number of buffers to add
+ */
+static void vnic_fill_receive_buffer(const nic_t *nic, int rbdr_free)
+{
+    int nic_vf = nic->nic_vf;
+    int rbdr = nic->rbdr;
+
+    BDK_CSR_INIT(rbdr_base, nic->node, BDK_NIC_QSX_RBDRX_BASE(nic_vf, rbdr));
+    BDK_CSR_INIT(rbdr_tail, nic->node, BDK_NIC_QSX_RBDRX_TAIL(nic_vf, rbdr));
+    BDK_TRACE(NIC, "%s: In Filling RBDR(%d, %d) base 0x%lx\n", nic->handle->name, nic->nic_vf, nic->rbdr, rbdr_base.u);
+
+    uint64_t *rbdr_ptr = bdk_phys_to_ptr(rbdr_base.u);
+    int loc = rbdr_tail.s.tail_ptr;
+    BDK_TRACE(NIC, "%s: In Filling RBDR(%d, %d) loc %d\n", nic->handle->name, nic->nic_vf, nic->rbdr, loc);
+
+    int added = 0;
+    for (int i = 0; i < rbdr_free; i++)
+    {
+        bdk_if_packet_t packet;
+        if (bdk_if_alloc(&packet, global_buffer_size))
+        {
+            bdk_error("%s: Failed to allocate buffer for RX ring (added %d)\n", nic->handle->name, added);
+            break;
+        }
+        rbdr_ptr[loc] = bdk_cpu_to_le64(packet.packet[0].s.address);
+        BDK_TRACE(NIC, "%s: In Filling RBDR(%d, %d) loc %d = 0x%lx\n", nic->handle->name, nic->nic_vf, nic->rbdr, loc, rbdr_ptr[loc]);
+        loc++;
+        loc &= RBDR_ENTRIES - 1;
+        added++;
+    }
+    BDK_WMB;
+    BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_RBDRX_DOOR(nic_vf, rbdr), added);
+    BDK_TRACE(NIC, "%s: In Filling RBDR(%d, %d) added %d\n", nic->handle->name, nic->nic_vf, nic->rbdr, added);
+}
+
+/**
+ * Setup a receive buffer descriptor ring (RBDR). Note that NIC share the RBDR if
+ * "share_cq" is set.
+ *
+ * @param nic    NIC to setup RBDR for
+ *
+ * @return Zero on success, negative on failure
+ */
+static int vnic_setup_rbdr(nic_t *nic)
+{
+    bool do_fill;
+
+    /* All devices using the same NIC VF use the same RBDRs. Don't fill them
+       for and ports except the first */
+    if (nic->handle->index)
+    {
+        do_fill = false;
+    }
+    else
+    {
+        BDK_TRACE(NIC, "%s: Setting up RBDR(%d, %d)\n", nic->handle->name, nic->nic_vf, nic->rbdr);
+        void *rbdr_base = memalign(BDK_CACHE_LINE_SIZE, 8 * RBDR_ENTRIES);
+        if (!rbdr_base)
+        {
+            bdk_error("%s: Failed to allocate memory for RBDR\n", nic->handle->name);
+            return -1;
+        }
+        /* Configure the receive buffer ring (RBDR) */
+        BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_RBDRX_BASE(nic->nic_vf, nic->rbdr),
+            bdk_ptr_to_phys(rbdr_base));
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_QSX_RBDRX_CFG(nic->nic_vf, nic->rbdr),
+            c.s.ena = 1;
+            c.s.ldwb = BDK_USE_DWB;
+            c.s.qsize = RBDR_ENTRIES_QSIZE;
+            c.s.lines = global_buffer_size / BDK_CACHE_LINE_SIZE);
+        do_fill = true;
+    }
+
+    BDK_TRACE(NIC, "%s: Setting up RQ(%d, %d)\n", nic->handle->name, nic->nic_vf, nic->rq);
+    /* Configure our vnic to use the RBDR */
+    /* Connect this RQ to the RBDR. Both the first and next buffers come from
+       the same RBDR */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_RQX_CFG(nic->nic_vf, nic->rq),
+        c.s.caching = 1; /* Allocate to L2 */
+        c.s.cq_qs = nic->nic_vf;
+        c.s.cq_idx = nic->cq;
+        c.s.rbdr_cont_qs = nic->nic_vf;
+        c.s.rbdr_cont_idx = nic->rbdr;
+        c.s.rbdr_strt_qs = nic->nic_vf;
+        c.s.rbdr_strt_idx = nic->rbdr);
+    /* NIC_PF_CQM_CFG is configure to drop everything if the CQ has 128 or
+       less entries available. Start backpressure when we have 256 or less */
+    int cq_bp = 256;
+    int rbdr_bp = 256;
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_RQX_BP_CFG(nic->nic_vf, nic->rq),
+        c.s.rbdr_bp_ena = 1;
+        c.s.cq_bp_ena = 1;
+        c.s.rbdr_bp = rbdr_bp * 256 / RBDR_ENTRIES; /* Zero means no buffers, 256 means lots available */
+        c.s.cq_bp = cq_bp * 256 / CQ_ENTRIES; /* Zero means full, 256 means idle */
+        c.s.bpid = nic->bpid);
+    /* Errata (NIC-21269) Limited NIC receive scenario verification */
+    /* RED drop set with pass=drop, so no statistical dropping */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_RQX_DROP_CFG(nic->nic_vf, nic->rq),
+        c.s.rbdr_red = 0;
+        c.s.cq_red = 0;
+        c.s.rbdr_pass = 0; /* Zero means no buffers, 256 means lots available */
+        c.s.rbdr_drop = 0;
+        c.s.cq_pass = 0; /* Zero means full, 256 means idle */
+        c.s.cq_drop = 0);
+
+    if (do_fill)
+    {
+        BDK_TRACE(NIC, "%s: Filling RBDR(%d, %d)\n", nic->handle->name, nic->nic_vf, nic->rbdr);
+        /* We probably don't have enough space to completely fill the RBDR. Use
+           1/8 of the buffers available */
+        int fill_num = bdk_config_get_int(BDK_CONFIG_NUM_PACKET_BUFFERS) / 8;
+        if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) fill_num = fill_num/3; /* CN83XX has more nics */
+        /* Note that RBDR must leave one spot empty */
+        if (fill_num > RBDR_ENTRIES - 1)
+            fill_num = RBDR_ENTRIES - 1;
+        vnic_fill_receive_buffer(nic, fill_num);
+    }
+
+    return 0;
+}
+
+/**
+ * Setup traffic shapping for a NIC. This put the shappers in passthrough mode
+ * where no shapping is applied.
+ *
+ * @param nic    NIC to configure shaping for
+ *
+ * @return Zero on success, negative on failure
+ */
+static int vnic_setup_tx_shaping(nic_t *nic)
+{
+    int tl1_index = -1;
+    int tl2_index = -1;
+    int tl3_index = -1;
+    int tl4_index = -1;
+    int nic_chan_e = -1;
+
+    BDK_TRACE(NIC, "%s: Setting up shaping(%d, %d)\n", nic->handle->name, nic->nic_vf, nic->sq);
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        /* TL1 feeds the DMA engines. One for each BGX */
+        tl1_index = nic->handle->interface;
+        /* TL2 feeds TL1 based on the top/bottom half. Use an independent TL1
+           entry for each BGX port */
+        tl2_index = tl1_index * 32 + nic->handle->index;
+        /* Each block of 4 TL3 feed TL2 */
+        tl3_index = tl2_index * 4;
+        /* Each block of 4 TL4 feed TL3 */
+        tl4_index = tl3_index * 4;
+        nic_chan_e = BDK_NIC_CHAN_E_BGXX_PORTX_CHX(nic->handle->interface, nic->handle->index, 0/*channel*/);
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+    {
+        switch (nic->ntype)
+        {
+            case BDK_NIC_TYPE_BGX:
+                tl1_index = BDK_NIC_LMAC_E_BGXX_LMACX(nic->handle->interface, nic->handle->index);
+                nic_chan_e = 0 ; /* Channel is lmac-relative */
+                break;
+            case BDK_NIC_TYPE_LBK:
+                tl1_index = BDK_NIC_LMAC_E_LBKX_CN83XX((nic->handle->interface == 3) ? 1 : 0);
+                nic_chan_e = nic->handle->index; /* Channel is lmac-relative */
+                break;
+            default:
+                bdk_error("%s: Unsupported NIC TYPE %d\n", nic->handle->name, nic->ntype);
+                return -1;
+        }
+        /* TL1 index by NIC_LMAC_E */
+        /* Set in above switch statement */
+        /* TL2 index is software defined, make it the same as TL1 for straight through */
+        tl2_index = tl1_index;
+        /* Each block of 4 TL3 feed TL2. This assumes there are never more than 4 ports per interface */
+        tl3_index = tl2_index * 4 + nic->handle->index;
+        /* TL4 index is the same as TL3, 1:1 hookup */
+        tl4_index = tl3_index;
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+    {
+        switch (nic->ntype)
+        {
+            case BDK_NIC_TYPE_BGX:
+                tl1_index = BDK_NIC_LMAC_E_BGXX_LMACX(nic->handle->interface, nic->handle->index);
+                nic_chan_e = BDK_NIC_CHAN_E_BGXX_LMACX_CHX(nic->handle->interface, nic->handle->index, 0/*channel*/);
+                break;
+            case BDK_NIC_TYPE_RGMII:
+                tl1_index = BDK_NIC_LMAC_E_RGXX_LMACX(nic->handle->interface, nic->handle->index);
+                nic_chan_e = 0; /* Channel is lmac-relative */
+                break;
+            case BDK_NIC_TYPE_LBK:
+                tl1_index = BDK_NIC_LMAC_E_LBKX_CN81XX(nic->handle->interface);
+                nic_chan_e = nic->handle->index; /* Channel is lmac-relative */
+                break;
+            default:
+                bdk_error("%s: Unsupported NIC TYPE %d\n", nic->handle->name, nic->ntype);
+                return -1;
+        }
+        /* TL1 index by NIC_LMAC_E */
+        /* Set in above switch statement */
+        /* TL2 index is software defined, make it the same as TL1 for straight through */
+        tl2_index = tl1_index;
+        /* Each block of 4 TL3 feed TL2. This assumes there are never more than 4 ports per interface */
+        tl3_index = tl2_index * 4 + nic->handle->index;
+        /* TL4 index is the same as TL3, 1:1 hookup */
+        tl4_index = tl3_index;
+    }
+    else
+    {
+        bdk_error("%s: Unsupported chip (NIC shaping)\n", nic->handle->name);
+        return -1;
+    }
+
+    /* Setup TL2 to TL1 mappings */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL2X_CFG(tl2_index),
+        c.s.rr_quantum = (MAX_MTU+4) / 4);
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL2X_PRI(tl2_index),
+        c.s.rr_pri = 0);
+    if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL2X_LMAC(tl2_index),
+            c.s.lmac = tl1_index);
+    }
+
+    /* TL3 feeds Tl2 */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL3AX_CFG(tl3_index / 4),
+        c.s.tl3a = tl2_index);
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL3X_CFG(tl3_index),
+        c.s.rr_quantum = (MAX_MTU+4) / 4);
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL3X_CHAN(tl3_index),
+        c.s.chan = nic_chan_e);
+
+    /* TL4 feeds TL3 */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL4AX_CFG(tl4_index / 4),
+            c.s.tl4a = tl3_index);
+    }
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_TL4X_CFG(tl4_index),
+        c.s.sq_qs = nic->nic_vf;
+        c.s.sq_idx = nic->sq;
+        c.s.rr_quantum = (MAX_MTU+4) / 4);
+
+    /* SQ feeds TL4 */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_SQX_CFG2(nic->nic_vf, nic->sq),
+        c.s.tl4 = tl4_index);
+
+    return 0;
+}
+
+/**
+ * Free the buffers in a packet to the RBDR used by the port
+ *
+ * @param priv   Determines which RBDR is used
+ * @param packet Packet to put in RBDR
+ */
+static void if_free_to_rbdr(bdk_if_packet_t *packet, nic_rbdr_state_t *vnic_rbdr_state)
+{
+    uint64_t *rbdr_ptr = vnic_rbdr_state->base;
+    int loc = vnic_rbdr_state->loc;
+
+    for (int s = 0; s < packet->segments; s++)
+    {
+        /* Make sure we strip off any padding added by the hardware in the address */
+        uint64_t address = packet->packet[s].s.address & -BDK_CACHE_LINE_SIZE;
+        rbdr_ptr[loc] = bdk_cpu_to_le64(address);
+        loc++;
+        loc &= RBDR_ENTRIES - 1;
+    }
+    vnic_rbdr_state->loc = loc;
+}
+
+/**
+ * Process a CQ receive entry
+ *
+ * @param node      Node containing the CQ
+ * @param vnic_rbdr_state
+ *                  Current RBDR state for the RBDR connected to the CQ
+ * @param cq_header CQ header to process
+ * @param use_cqe_rx2
+ *                  True of the CQ will contain an extended CQE_RX2 header
+ *
+ * @return Returns the amount the RBDR doorbell needs to increment
+ */
+static int if_process_complete_rx(int node, nic_rbdr_state_t *vnic_rbdr_state, const union bdk_nic_cqe_rx_s *cq_header, const union bdk_nic_cqe_rx_s *cq_header_le, bool use_cqe_rx2)
+{
+    nic_node_state_t *node_state = global_node_state[node];
+    int nic_id = cq_header->s.rq_qs * 8 + cq_header->s.rq_idx;
+
+    bdk_if_packet_t packet;
+    packet.length = cq_header->s.len;
+    packet.segments = cq_header->s.rb_cnt;
+    packet.if_handle = node_state->nic_map[nic_id]->handle;
+    /* Combine the errlev and errop into a single 11 bit number. Errop
+       is 8 bits, so errlev will be in the top byte */
+    packet.rx_error = cq_header->s.errlev;
+    packet.rx_error <<= 8;
+    packet.rx_error |= cq_header->s.errop;
+
+    const uint16_t *rb_sizes = (void*)cq_header_le + 24; /* Offset of RBSZ0 */
+    const uint64_t *rb_addresses = (uint64_t*)(cq_header_le+1);
+    /* Update offset if nic_cqe_rx2_s is used */
+    if (use_cqe_rx2)
+        rb_addresses += sizeof(union bdk_nic_cqe_rx2_s) / 8;
+    int segment_length = 0;
+
+    for (int s = 0; s < packet.segments; s++)
+    {
+        uint64_t addr = bdk_le64_to_cpu(rb_addresses[s]);
+        BDK_PREFETCH(bdk_phys_to_ptr(addr), 0);
+        packet.packet[s].u = addr;
+        packet.packet[s].s.size = bdk_le16_to_cpu(rb_sizes[s]);
+        BDK_TRACE(NIC, "    Receive segment size %d address 0x%lx\n", packet.packet[s].s.size, addr);
+        segment_length += packet.packet[s].s.size;
+    }
+
+    /* If we ran out of buffer the packet could be truncated */
+    if (segment_length < packet.length)
+        packet.length = segment_length;
+
+    if (bdk_likely(packet.if_handle))
+    {
+        /* Do RX stats in software as it is fast and I don't really trust
+           the hardware. The hardware tends to count packets that are received
+           and dropped in some weird way. Hopefully the hardware counters
+           looking for drops can find these. It is important that they
+           aren't counted as good */
+        packet.if_handle->stats.rx.packets++;
+        packet.if_handle->stats.rx.octets += packet.length;
+        if (packet.if_handle->flags & BDK_IF_FLAGS_HAS_FCS)
+            packet.if_handle->stats.rx.octets += 4;
+        if (packet.rx_error)
+            packet.if_handle->stats.rx.errors++;
+        bdk_if_dispatch_packet(&packet);
+    }
+    else
+    {
+        bdk_error("Unable to determine interface for NIC %d.%d\n", cq_header->s.rq_qs, cq_header->s.rq_idx);
+    }
+
+    if_free_to_rbdr(&packet, vnic_rbdr_state);
+    return packet.segments;
+}
+
+/**
+ * Process all entries in a completion queue (CQ). Note that a CQ is shared
+ * among many ports, so packets will be dispatch for other port handles.
+ *
+ * @param handle Interface handle connected to the CQ
+ *
+ * @return Number of packets received
+ */
+static void if_receive(int unused, void *hand)
+{
+    const nic_t *nic = hand;
+
+    /* Sadly the hardware team decided to change the meaning of NIC_PF_RX_CFG
+       for chips after CN88XX. This stupid spec change was really hard to
+       find */
+    bool use_cqe_rx2 = !CAVIUM_IS_MODEL(CAVIUM_CN88XX);
+
+    /* Figure out which completion queue we're using */
+    int nic_vf = nic->nic_vf;
+    int rbdr = nic->rbdr;
+    int cq = nic->cq;
+
+    BDK_CSR_INIT(cq_base, nic->node, BDK_NIC_QSX_CQX_BASE(nic_vf, cq));
+    const void *cq_ptr = bdk_phys_to_ptr(cq_base.u);
+
+    /* Find the current CQ location */
+    BDK_CSR_INIT(cq_head, nic->node, BDK_NIC_QSX_CQX_HEAD(nic_vf, cq));
+    int loc = cq_head.s.head_ptr;
+
+    /* Store the RBDR data locally to avoid contention */
+    BDK_CSR_INIT(rbdr_base, nic->node, BDK_NIC_QSX_RBDRX_BASE(nic_vf, rbdr));
+    BDK_CSR_INIT(rbdr_tail, nic->node, BDK_NIC_QSX_RBDRX_TAIL(nic_vf, rbdr));
+    nic_rbdr_state_t vnic_rbdr_state;
+    vnic_rbdr_state.base = bdk_phys_to_ptr(rbdr_base.u);
+    vnic_rbdr_state.loc = rbdr_tail.s.tail_ptr;
+
+    BDK_TRACE(NIC, "%s: Receive thread for CQ(%d, %d) started\n", nic->handle->name, nic->nic_vf, nic->cq);
+
+    while (1)
+    {
+        /* Exit immediately if the CQ is empty */
+        BDK_CSR_INIT(cq_status, nic->node, BDK_NIC_QSX_CQX_STATUS(nic_vf, cq));
+        int pending_count = cq_status.s.qcount;
+        if (bdk_likely(!pending_count))
+        {
+            bdk_wait_usec(1);
+            continue;
+        }
+
+        /* Loop through all pending CQs */
+        int rbdr_doorbell = 0;
+        int count = 0;
+        const union bdk_nic_cqe_rx_s *cq_next = cq_ptr + loc * 512;
+        BDK_TRACE(NIC, "%s: Receive thread CQ(%d, %d): %d pending\n", nic->handle->name, nic->nic_vf, nic->cq, pending_count);
+        while (count < pending_count)
+        {
+            const union bdk_nic_cqe_rx_s *cq_header = cq_next;
+            const union bdk_nic_cqe_rx_s *cq_header_le = cq_header;
+#if __BYTE_ORDER == __BIG_ENDIAN
+            union bdk_nic_cqe_rx_s cq_be;
+            for (int i = 0; i < 6; i++)
+                cq_be.u[i] = bdk_le64_to_cpu(cq_header_le->u[i]);
+            cq_header = &cq_be;
+#endif
+            BDK_TRACE(NIC, "%s: Receive HDR[%p] = 0x%lx 0x%lx 0x%lx 0x%lx\n",
+                nic->handle->name, cq_header_le, cq_header->u[0], cq_header->u[1], cq_header->u[2], cq_header->u[3]);
+            loc++;
+            loc &= CQ_ENTRIES - 1;
+            cq_next = cq_ptr + loc * 512;
+            BDK_PREFETCH(cq_next, 0);
+            if (bdk_likely(cq_header->s.cqe_type == BDK_NIC_CQE_TYPE_E_RX))
+                rbdr_doorbell += if_process_complete_rx(nic->node, &vnic_rbdr_state, cq_header, cq_header_le, use_cqe_rx2);
+            else
+                bdk_error("Unsupported CQ header type %d\n", cq_header->s.cqe_type);
+            count++;
+        }
+        /* Ring the RBDR doorbell for all packets */
+        BDK_WMB;
+        BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_RBDRX_DOOR(nic_vf, rbdr), rbdr_doorbell);
+        /* Free all the CQs that we've processed */
+        BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_CQX_DOOR(nic_vf, cq), count);
+        /* Yield before going through more packets. The low core count chips
+           don't have enough cores to dedicate for TX and RX. This forces
+           sharing under load. If there are enough cores, the yield does
+           nothing */
+        bdk_thread_yield();
+    }
+}
+
+/**
+ * Configure NIC for a specific port. This is called for each
+ * port on every interface that connects to NIC.
+ *
+ * @param handle Handle for port to config
+ * @param ntype  Type of LMAC this NIC connects to
+ * @param lmac_credits
+ *               Size of the LMAC buffer in bytes. Used to configure the number of credits to
+ *               setup between the NIC and LMAC
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_nic_port_init(bdk_if_handle_t handle, bdk_nic_type_t ntype, int lmac_credits)
+{
+    int nic_chan_idx_e;     /* Flow channel for the CPI */
+    bool has_rx_nic = (-1 == handle->pki_channel);  /* true when nic rx channel exists - may be BGX or LBK-NIC*/
+    bool has_tx_nic = (-1 == handle->pko_queue);  /* true when nic tx channel exists - may be BGX or LBK-NIC*/
+    int nic_intf_e = -1;         /* Interface enumeration */
+    int nic_intf_block_e;   /* Interface Block ID Enumeration */
+    int nic_lmac_e=-1;         /* LMAC enumeration */
+
+    if (global_buffer_size == 0)
+        global_buffer_size = bdk_config_get_int(BDK_CONFIG_PACKET_BUFFER_SIZE);
+
+    if (!has_rx_nic && !has_tx_nic) return 0;
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        /* Flow here is a compressed NIC_CHAN_E enum value. Flow is bit[8] and
+           bit[6:0] from NIC_CHAN_E. This works out as:
+           bit 7: BGX interface number(0-1)
+           bit 6:4: BGX port number(0-3)
+           bit 3:0: BGX channel on a port (0-15) */
+        nic_chan_idx_e = (handle->interface) ? 0x80 : 0x00;
+        nic_chan_idx_e += handle->index * 16;
+        nic_chan_idx_e += 0; /* channel */
+        nic_intf_e = BDK_NIC_INTF_E_BGXX(handle->interface);
+        nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_BGXX_BLOCK(handle->interface);
+        nic_lmac_e = BDK_NIC_LMAC_E_BGXX_LMACX(handle->interface, handle->index);
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+    {
+        switch (ntype)
+        {
+            case BDK_NIC_TYPE_BGX:
+                nic_chan_idx_e = BDK_NIC_CHAN_IDX_E_BGXX_LMACX_CHX(handle->interface, handle->index, 0/*channel*/);
+                nic_intf_e = BDK_NIC_INTF_E_BGXX(handle->interface);
+                nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_BGXX(handle->interface);
+                nic_lmac_e = BDK_NIC_LMAC_E_BGXX_LMACX(handle->interface, handle->index);
+                break;
+            case BDK_NIC_TYPE_LBK:
+                nic_chan_idx_e = BDK_NIC_CHAN_IDX_E_LBKX_CHX_CN83XX((handle->interface == 3) ? 1 : 0, handle->index);
+                // rx interface
+                if (3 == handle->interface) {
+                    nic_intf_e = BDK_NIC_INTF_E_LBKX_CN83XX(1);
+                } else if  (2 == handle->interface) {
+                    nic_intf_e = BDK_NIC_INTF_E_LBKX_CN83XX(0);
+                }
+                nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_LBKX(handle->interface);
+                // tx interface
+                if (3 == handle->interface) {
+                    nic_lmac_e = BDK_NIC_LMAC_E_LBKX_CN83XX(1);
+                } else if  (1 == handle->interface) {
+                    nic_lmac_e = BDK_NIC_LMAC_E_LBKX_CN83XX(0);
+                }
+                break;
+            default:
+                bdk_error("%s: Unsupported NIC TYPE %d\n", handle->name, ntype);
+                return -1;
+        }
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+    {
+        switch (ntype)
+        {
+            case BDK_NIC_TYPE_BGX:
+                nic_chan_idx_e = BDK_NIC_CHAN_IDX_E_BGXX_LMACX_CHX(handle->interface, handle->index, 0/*channel*/);
+                nic_intf_e = BDK_NIC_INTF_E_BGXX(handle->interface);
+                nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_BGXX(handle->interface);
+                nic_lmac_e = BDK_NIC_LMAC_E_BGXX_LMACX(handle->interface, handle->index);
+                break;
+            case BDK_NIC_TYPE_RGMII:
+                nic_chan_idx_e = BDK_NIC_CHAN_IDX_E_RGXX_LMACX_CHX(handle->interface, handle->index, 0/*channel*/);
+                nic_intf_e = BDK_NIC_INTF_E_RGXX(handle->index);
+                nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_BGXX(handle->interface + 2);
+                nic_lmac_e = BDK_NIC_LMAC_E_RGXX_LMACX(handle->interface, handle->index);
+                break;
+            case BDK_NIC_TYPE_LBK:
+                nic_chan_idx_e = BDK_NIC_CHAN_IDX_E_LBKX_CHX_CN81XX(handle->interface, handle->index);
+                nic_intf_e = BDK_NIC_INTF_E_LBKX_CN81XX(handle->interface);
+                nic_intf_block_e = BDK_NIC_INTF_BLOCK_E_LBKX(handle->interface);
+                nic_lmac_e = BDK_NIC_LMAC_E_LBKX_CN81XX(handle->interface);
+                break;
+            default:
+                bdk_error("%s: Unsupported NIC TYPE %d\n", handle->name, ntype);
+                return -1;
+        }
+    }
+    else
+    {
+        bdk_error("%s: Unsupported chip (NIC init)\n", handle->name);
+        return -1;
+    }
+
+    /* Make sure the node global state has been allocated */
+    if (global_node_state[handle->node] == NULL)
+    {
+        int num_nic_vf;
+        if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+        {
+            /* NIC_PF_CONST1 didn't exist on this chip */
+            num_nic_vf = 128;
+        }
+        else
+        {
+            BDK_CSR_INIT(nic_pf_const1, handle->node, BDK_NIC_PF_CONST1);
+            num_nic_vf = nic_pf_const1.s.vnics;
+        }
+        global_node_state[handle->node] = calloc(1, sizeof(nic_node_state_t) + sizeof(handle) * num_nic_vf * 8);
+        if (global_node_state[handle->node] == NULL)
+        {
+            bdk_error("N%d.NIC: Failed to allocate node state\n", handle->node);
+            return -1;
+        }
+        global_node_state[handle->node]->num_nic_vf = num_nic_vf;
+    }
+    nic_node_state_t *node_state = global_node_state[handle->node];
+
+    /* See if we have a free VF */
+    if (!handle->index && (node_state->next_free_nic_vf >= node_state->num_nic_vf))
+    {
+        bdk_error("N%d.NIC: Ran out of NIC VFs\n", handle->node);
+        return -1;
+    }
+
+    /* VNIC setup requirements
+       The code in this file makes the following assumptions:
+       1) One RBDR for each CQ. No locking is done on RBDR
+       2) A CQ can be shared across multiple ports, saving space as the
+            cost of performance.
+       3) One SQ per physical port, no locking on TX
+       4) One RQ per physical port, many RQ may share RBDR/CQ
+
+        Current setup without DRAM:
+        1) One NIC VF is used for an entire interface (BGX, LBK). The variable
+            nic_vf represents the NIC virtual function.
+        2) SQs are allocated one per port. SQ index equals handle->index
+        3) RQs are allocated one per port. RQ index equals handle->index
+        4) One CQ is allcoated per entire interface, using index 0
+        5) One RBDR is used for the CQ, index 0
+
+        Current setup with DRAM:
+        FIXME: Same as without DRAM. There are not enough RBDR to have
+            independent CQs without locking.
+       */
+    void *sq_memory = NULL;
+    if (has_tx_nic) {
+        sq_memory = memalign(BDK_CACHE_LINE_SIZE, 16 * SQ_ENTRIES);
+        if (!sq_memory)
+        {
+            bdk_error("%s: Unable to allocate queues\n", handle->name);
+            return -1;
+        }
+    }
+    nic_t *nic = calloc(1, sizeof(nic_t));
+    if (!nic)
+    {
+        if (sq_memory) free(sq_memory);
+        bdk_error("%s: Unable to NIC state\n", handle->name);
+        return -1;
+    }
+
+    /* Fill in the various NIC indexes */
+    nic->node = handle->node;
+    nic->ntype = ntype;
+    if (handle->index)
+        nic->nic_vf = node_state->next_free_nic_vf - 1; /* reuse last one */
+    else
+        nic->nic_vf = node_state->next_free_nic_vf++; /* New nic */
+    nic->sq = handle->index;
+    nic->cq = 0;
+    nic->rq = handle->index;
+    nic->rbdr = 0;
+    nic->bpid = node_state->next_free_bpid++;
+    nic->handle = handle;
+    BDK_TRACE(NIC, "%s: Creating NIC(%d, sq=%d, cq=%d, rq=%d, rbdr=%d, bpid=%d)\n",
+        nic->handle->name, nic->nic_vf, nic->sq, nic->cq, nic->rq, nic->rbdr, nic->bpid);
+
+    /* Connect this NIC to the handle */
+    handle->nic_id = nic->nic_vf * 8 + nic->rq;
+    node_state->nic_map[handle->nic_id] = nic;
+
+    /* Enable global BP state updates */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_BP_CFG,
+        c.s.bp_poll_ena = 1;
+        c.s.bp_poll_dly = 3);
+
+    /* Enable interface level backpresure */
+    if (-1 != nic_intf_e) {
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_INTFX_BP_CFG(nic_intf_e),
+            c.s.bp_ena = 1;
+            c.s.bp_type = ((nic->ntype == BDK_NIC_TYPE_BGX) ||
+                           (nic->ntype == BDK_NIC_TYPE_RGMII)) ? 0 : 1; /* 0=BGX, 1=LBK/TNS */
+            c.s.bp_id = nic_intf_block_e);
+    }
+    if (has_tx_nic) {
+        /* Configure the submit queue (SQ) */
+        nic->sq_base = sq_memory;
+        nic->sq_loc = 0;
+        nic->sq_available = SQ_ENTRIES;
+        BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_SQX_BASE(nic->nic_vf, nic->sq),
+                      bdk_ptr_to_phys(sq_memory));
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_QSX_SQX_CFG(nic->nic_vf, nic->sq),
+                       if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+                           c.s.cq_limit = 1;
+                       c.s.ena = 1;
+                       c.s.ldwb = BDK_USE_DWB;
+                       c.s.qsize = SQ_ENTRIES_QSIZE);
+    }
+    int cpi=0;
+    int rssi=0;
+    if (has_rx_nic) {
+        /* Configure the receive queue (RQ) */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_QSX_RQ_GEN_CFG(nic->nic_vf),
+                       c.s.vlan_strip = 0;
+                       c.s.len_l4 = 0;
+                       c.s.len_l3 = 0;
+                       c.s.csum_l4 = 0;
+                       c.s.ip6_udp_opt = 0;
+                       c.s.splt_hdr_ena = 0;
+                       c.s.cq_hdr_copy = 0;
+                       c.s.max_tcp_reass = 0;
+                       c.s.cq_pkt_size = 0;
+                       c.s.later_skip = 0;
+                       c.s.first_skip = 0);
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_QSX_RQX_CFG(nic->nic_vf, nic->rq),
+                       c.s.ena = 1;
+                       c.s.tcp_ena = 0);
+
+        cpi = node_state->next_free_cpi++;  /* Allocate a new Channel Parse Index (CPI) */
+        rssi = node_state->next_free_rssi++;/* Allocate a new Receive-Side Scaling Index (RSSI) */
+        /* NIC_CHAN_E hard mapped to "flow". Flow chooses the CPI */
+
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_CHANX_RX_CFG(nic_chan_idx_e),
+                       c.s.cpi_alg = BDK_NIC_CPI_ALG_E_NONE;
+                       c.s.cpi_base = cpi);
+        /* Setup backpressure */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_CHANX_RX_BP_CFG(nic_chan_idx_e),
+                       c.s.ena = 1;
+                       c.s.bpid = nic->bpid);
+    }
+    if ( has_tx_nic) {
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_CHANX_TX_CFG(nic_chan_idx_e),
+            c.s.bp_ena = 1);
+    }
+
+    if (has_rx_nic)  {
+        /* CPI is the output of the above alogrithm, this is used to lookup the
+           VNIC for receive and RSSI */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_CPIX_CFG(cpi),
+                       c.cn88xxp1.vnic = nic->nic_vf; /* TX and RX use the same VNIC */
+                       c.cn88xxp1.rss_size = 0; /* RSS hash is disabled */
+                       c.s.padd = 0; /* Used if we have multiple channels per port */
+                       c.cn88xxp1.rssi_base = rssi); /* Base RSSI */
+
+        if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+        {
+            /* CN88XX pass 2 moved some fields to a different CSR */
+            BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_MPIX_CFG(cpi),
+                           c.s.vnic = nic->nic_vf; /* TX and RX use the same VNIC */
+                           c.s.rss_size = 0; /* RSS hash is disabled */
+                           c.s.rssi_base = rssi); /* Base RSSI */
+        }
+
+        /* The RSSI is used to determine which Receive Queue (RQ) we use */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_RSSIX_RQ(rssi),
+                       c.s.rq_qs = nic->nic_vf;
+                       c.s.rq_idx = nic->rq);
+        /* Set the min and max packet size. PKND comes from BGX. It is always zero
+           for now */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_PKINDX_CFG(handle->pknd),
+                       c.s.lenerr_en = 0;
+                       c.s.minlen = 0;
+                       c.s.maxlen = 65535);
+    }
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        /* Bypass the TNS */
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_INTFX_SEND_CFG(handle->interface),
+           c.s.tns_nonbypass = 0;
+           c.s.block = 0x8 + handle->interface);
+    }
+
+    /* Errata (NIC-21858) If NIC_PF_QS()_CFG ENA is set after RRM enabled...RRM breaks */
+    /* Do global vnic init */
+    BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_QSX_CFG(nic->nic_vf),
+        c.s.ena = 1;
+        c.s.vnic = nic->nic_vf);
+
+    if (has_tx_nic && vnic_setup_tx_shaping(nic))
+        return -1;
+
+    /* Completion queue may be used by both tx and rx.
+    ** Define it even if only one of rx/tx is in use
+    */
+    if (vnic_setup_cq(nic))
+        return -1;
+    /* RBDR is defined regardless of rx_nic to avoid possible backpressure */
+    if ( vnic_setup_rbdr(nic))
+        return -1;
+
+    /* Program LMAC credits */
+    if ((has_tx_nic) && (-1 != nic_lmac_e)) {
+        int credit;
+        if ((BDK_NIC_TYPE_LBK == nic->ntype) && CAVIUM_IS_MODEL(CAVIUM_CN83XX) )
+            credit = 512; /* HRM guidance */
+        else
+            credit = (lmac_credits - MAX_MTU) / 16;
+        BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_LMACX_CREDIT(nic_lmac_e),
+            c.s.cc_unit_cnt = credit;
+            c.s.cc_packet_cnt = 0x1ff;
+            c.s.cc_enable = 1);
+
+        /* Pad packets to 60 bytes, 15 32bit words (before FCS) */
+        if (nic->ntype != BDK_NIC_TYPE_LBK)
+            BDK_CSR_MODIFY(c, nic->node, BDK_NIC_PF_LMACX_CFG(nic_lmac_e),
+                           c.s.min_pkt_size = 15);
+    }
+    /* Create a receive thread if this handle has its own CQ/RBDR */
+    if (handle->index == 0)
+    {
+        /* FIXME
+         * At this time thread monitors both CQ and RBDR and uses it only for receive
+         * Setting up RBDR for tx only nics is wasteful.
+         * When nic_tx in bdk starts using CQ, thread needs to change
+         */
+        if (has_rx_nic && bdk_thread_create(nic->node, 0, if_receive, 0, nic, 0))
+        {
+            bdk_error("%s: Failed to allocate receive thread\n", handle->name);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Send a packet
+ *
+ * @param handle Handle of port to send on
+ * @param packet Packet to send
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_nic_transmit(bdk_if_handle_t handle, const bdk_if_packet_t *packet)
+{
+    /* The SQ can't be filled completely as it reguires at least one free
+       entry so the head and pointer don't look like empty. SQ_SLOP is the
+       amount of SQ space we reserve to make sure of this */
+    const int SQ_SLOP = 1;
+    const nic_node_state_t *node_state = global_node_state[handle->node];
+    nic_t *nic = node_state->nic_map[handle->nic_id];
+    BDK_TRACE(NIC, "%s: Transmit packet of %d bytes, %d segments\n",
+        nic->handle->name, packet->length, packet->segments);
+
+    /* Update the SQ available if we're out of space. The NIC should have sent
+       packets, making more available. This allows us to only read the STATUS
+       CSR when really necessary, normally using the L1 cached value */
+    if (nic->sq_available < packet->segments + 1 + SQ_SLOP)
+    {
+        BDK_CSR_INIT(sq_status, nic->node, BDK_NIC_QSX_SQX_STATUS(nic->nic_vf, nic->sq));
+        nic->sq_available = SQ_ENTRIES - sq_status.s.qcount;
+        /* Re-Check for space. A packets is a header plus its segments */
+        if (nic->sq_available < packet->segments + 1 + SQ_SLOP)
+        {
+            BDK_TRACE(NIC, "%s: Transmit fail, queue full\n", nic->handle->name);
+            return -1;
+        }
+    }
+
+    /* Build the command */
+    void *sq_ptr = nic->sq_base;
+    int loc = nic->sq_loc;
+    union bdk_nic_send_hdr_s send_hdr;
+    send_hdr.u[0] = 0;
+    send_hdr.u[1] = 0;
+    send_hdr.s.subdc = BDK_NIC_SEND_SUBDC_E_HDR;
+    send_hdr.s.subdcnt = packet->segments;
+    send_hdr.s.total = packet->length;
+    switch (packet->packet_type)
+    {
+        case BDK_IF_TYPE_UNKNOWN:
+            break;
+        case BDK_IF_TYPE_UDP4:
+            send_hdr.s.ckl3 = 1;        /* L3 - IPv4 checksum enable */
+            send_hdr.s.l3ptr = 14;      /* L2 header is 14 bytes */
+            send_hdr.s.ckl4 = BDK_NIC_SEND_CKL4_E_UDP; /* L4 - UDP checksum enable */
+            send_hdr.s.l4ptr = 14 + 20; /* 14 bytes L2 + 20 bytes IPv4 */
+            break;
+        case BDK_IF_TYPE_TCP4:
+            send_hdr.s.ckl3 = 1;        /* L3 - IPv4 checksum enable */
+            send_hdr.s.l3ptr = 14;      /* L2 header is 14 bytes */
+            send_hdr.s.ckl4 = BDK_NIC_SEND_CKL4_E_TCP; /* L4 - TCP checksum enable */
+            send_hdr.s.l4ptr = 14 + 20; /* 14 bytes L2 + 20 bytes IPv4 */
+            if (packet->mtu)
+            {
+                int headers = 14 + 20 + 20;
+                send_hdr.s.tso = 1;     /* Use TCP offload */
+                send_hdr.s.tso_sb = headers; /* 14 bytes L2 + 20 bytes IPv4, 20 bytes TCP */
+                send_hdr.s.tso_mps = packet->mtu - headers; /* Max TCP data payload size */
+            }
+            break;
+    }
+    volatile uint64_t *wptr = (uint64_t *)(sq_ptr + loc * 16);
+    wptr[0] = bdk_cpu_to_le64(send_hdr.u[0]);
+    wptr[1] = bdk_cpu_to_le64(send_hdr.u[1]);
+    BDK_TRACE(NIC, "%s: Transmit HDR[%p] = 0x%lx 0x%lx\n",
+        nic->handle->name, sq_ptr + loc * 16, send_hdr.u[0], send_hdr.u[1]);
+    loc++;
+    loc &= SQ_ENTRIES - 1;
+    for (int s = 0; s < packet->segments; s++)
+    {
+        union bdk_nic_send_gather_s gather;
+        gather.u[0] = 0;
+        gather.u[1] = 0;
+        gather.s.addr = packet->packet[s].s.address;
+        gather.s.subdc = BDK_NIC_SEND_SUBDC_E_GATHER;
+        gather.s.ld_type = (BDK_USE_DWB) ? BDK_NIC_SEND_LD_TYPE_E_LDWB : BDK_NIC_SEND_LD_TYPE_E_LDD;
+        gather.s.size = packet->packet[s].s.size;
+        wptr = (uint64_t *)(sq_ptr + loc * 16);
+        wptr[0] = bdk_cpu_to_le64(gather.u[0]);
+        wptr[1] = bdk_cpu_to_le64(gather.u[1]);
+        BDK_TRACE(NIC, "%s: Transmit Gather[%p] = 0x%lx 0x%lx\n",
+            nic->handle->name, sq_ptr + loc * 16, gather.u[0], gather.u[1]);
+        loc++;
+        loc &= SQ_ENTRIES - 1;
+    }
+
+    BDK_WMB;
+
+    /* Ring the doorbell */
+    BDK_CSR_WRITE(nic->node, BDK_NIC_QSX_SQX_DOOR(nic->nic_vf, nic->sq),
+        packet->segments + 1);
+    BDK_TRACE(NIC, "%s: Transmit Doorbell %d\n", nic->handle->name, packet->segments + 1);
+
+    /* Update our cached state */
+    nic->sq_available -= packet->segments + 1;
+    nic->sq_loc = loc;
+    if (handle->iftype != BDK_IF_BGX) {
+        /* Update stats as we do them in software for non-BGX */
+        handle->stats.tx.packets++;
+        handle->stats.tx.octets += packet->length;
+        if (handle->flags & BDK_IF_FLAGS_HAS_FCS)
+            handle->stats.tx.octets += 4;
+    }
+    return 0;
+}
+
+/**
+ * Get the current TX queue depth. Note that this operation may be slow
+ * and adversly affect packet IO performance.
+ *
+ * @param handle Port to check
+ *
+ * @return Depth of the queue in packets
+ */
+int bdk_nic_get_queue_depth(bdk_if_handle_t handle)
+{
+    const nic_node_state_t *node_state = global_node_state[handle->node];
+    const nic_t *nic = node_state->nic_map[handle->nic_id];
+    BDK_CSR_INIT(sq_status, nic->node, BDK_NIC_QSX_SQX_STATUS(nic->nic_vf, nic->sq));
+    return sq_status.s.qcount;
+}
+
+/**
+ * Query NIC and fill in the transmit stats for the supplied
+ * interface handle.
+ *
+ * @param handle Port handle
+ */
+void bdk_nic_fill_tx_stats(bdk_if_handle_t handle)
+{
+    const int vnic = handle->nic_id >> 3;
+
+    /* Transmit stats are done in software due to CN81XX not having enough NICs */
+
+    /* Note drops are shared across a BGX. People will be confused */
+    BDK_CSR_INIT(drps, handle->node, BDK_NIC_VNICX_TX_STATX(vnic, BDK_NIC_STAT_VNIC_TX_E_TX_DROP));
+    handle->stats.tx.dropped_packets = bdk_update_stat_with_overflow(drps.u, handle->stats.tx.dropped_packets, 48);
+    /* Dropped Octets are not available */
+}
+
+/**
+ * Query NIC and fill in the receive stats for the supplied
+ * interface handle.
+ *
+ * @param handle Port handle
+ */
+void bdk_nic_fill_rx_stats(bdk_if_handle_t handle)
+{
+    /* Account for RX FCS */
+    const int bytes_off_rx = (handle->flags & BDK_IF_FLAGS_HAS_FCS) ? 4 : 0;
+    const int vnic = handle->nic_id >> 3;
+
+    /* Note stats are shared across a BGX. People will be confused */
+
+    /* Read the RX statistics. These do not include the ethernet FCS */
+    BDK_CSR_INIT(rx_red, handle->node, BDK_NIC_VNICX_RX_STATX(vnic, BDK_NIC_STAT_VNIC_RX_E_RX_RED));
+    BDK_CSR_INIT(rx_red_octets, handle->node, BDK_NIC_VNICX_RX_STATX(vnic, BDK_NIC_STAT_VNIC_RX_E_RX_RED_OCTS));
+    BDK_CSR_INIT(rx_ovr, handle->node, BDK_NIC_VNICX_RX_STATX(vnic, BDK_NIC_STAT_VNIC_RX_E_RX_ORUN));
+    BDK_CSR_INIT(rx_ovr_octets, handle->node, BDK_NIC_VNICX_RX_STATX(vnic, BDK_NIC_STAT_VNIC_RX_E_RX_ORUN_OCTS));
+    uint64_t drops = rx_red.u + rx_ovr.u;
+    uint64_t drop_octets = rx_red_octets.u + rx_ovr_octets.u;
+
+    /* Drop and error counters */
+    handle->stats.rx.dropped_octets -= handle->stats.rx.dropped_packets * bytes_off_rx;
+    handle->stats.rx.dropped_octets = bdk_update_stat_with_overflow(drop_octets, handle->stats.rx.dropped_octets, 48);
+    handle->stats.rx.dropped_packets = bdk_update_stat_with_overflow(drops, handle->stats.rx.dropped_packets, 48);
+    handle->stats.rx.dropped_octets += handle->stats.rx.dropped_packets * bytes_off_rx;
+
+    /* Normal RX stats are done by software on receive */
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie-cn8xxx.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie-cn8xxx.c
new file mode 100644
index 0000000..16034d2
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie-cn8xxx.c
@@ -0,0 +1,1263 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-arch/bdk-csrs-dtx.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-gic.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-arch/bdk-csrs-pcierc.h"
+#include "libbdk-arch/bdk-csrs-sli.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
+#include "libbdk-hal/bdk-pcie.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/bdk-utils.h"
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-ecam.h"
+
+/**
+ * Return the number of possible PCIe ports on a node. The actual number
+ * of configured ports may be less and may also be disjoint.
+ *
+ * @param node   Node to query
+ *
+ * @return Number of PCIe ports that are possible
+ */
+int bdk_pcie_get_num_ports(bdk_node_t node)
+{
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+        return 6;
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+        return 4;
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+        return 3;
+    else
+        return 0;
+}
+
+
+/**
+ * Given a PCIe port, determine which SLI controls its memory regions
+ *
+ * @param node      Node for the PCIe port
+ * @param pcie_port The PCIe port
+ * @param sli       The SLI index is written to this integer pointer
+ * @param sli_group The index of the PCIe port on the SLI is returned here. This is a sequencial
+ *                  number for each PCIe on an SLI. Use this to index SLI regions.
+ */
+static void __bdk_pcie_get_sli(bdk_node_t node, int pcie_port, int *sli, int *sli_group)
+{
+    /* This mapping should be determined by find the SLI number on the
+       same ECAM bus as the PCIERC bridge. That is fairly complex, so it is
+       hardcoded for now */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        /* Ports 0-2 goto SLI0, ports 3-5 goto SLI1 */
+        *sli = (pcie_port >= 3) ? 1 : 0;
+        *sli_group = pcie_port - *sli * 3;
+        return;
+    }
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+    {
+        /* Only one SLI */
+        *sli = 0;
+        *sli_group = pcie_port;
+        return;
+    }
+    else
+        bdk_fatal("Unable to determine SLI for PCIe port. Update __bdk_pcie_get_sli()\n");
+}
+
+/**
+ * Return the Core physical base address for PCIe MEM access. Memory is
+ * read/written as an offset from this address.
+ *
+ * @param node      Node to use in a Numa setup
+ * @param pcie_port PCIe port the memory is on
+ * @param mem_type  Type of memory
+ *
+ * @return 64bit physical address for read/write
+ */
+uint64_t bdk_pcie_get_base_address(bdk_node_t node, int pcie_port, bdk_pcie_mem_t mem_type)
+{
+    /* See __bdk_pcie_sli_initialize() for a description about how SLI regions work */
+    int sli;
+    int sli_group;
+    __bdk_pcie_get_sli(node, pcie_port, &sli, &sli_group);
+    int region = (sli_group << 6) | (mem_type << 4);
+    union bdk_sli_s2m_op_s s2m_op;
+    s2m_op.u = 0;
+    s2m_op.s.io = 1;
+    s2m_op.s.node = node;
+    s2m_op.s.did_hi = 0x8 + sli;
+    s2m_op.s.region = region;
+    return s2m_op.u;
+}
+
+/**
+ * Size of the Mem address region returned at address
+ * bdk_pcie_get_base_address()
+ *
+ * @param node      Node to use in a Numa setup
+ * @param pcie_port PCIe port the IO is for
+ * @param mem_type  Type of memory
+ *
+ * @return Size of the Mem window
+ */
+uint64_t bdk_pcie_get_base_size(bdk_node_t node, int pcie_port, bdk_pcie_mem_t mem_type)
+{
+    return 1ull << 36;
+}
+
+/**
+ * @INTERNAL
+ * Initialize the RC config space CSRs
+ *
+ * @param pcie_port PCIe port to initialize
+ */
+static void __bdk_pcie_rc_initialize_config_space(bdk_node_t node, int pcie_port)
+{
+    int sli;
+    int sli_group;
+    __bdk_pcie_get_sli(node, pcie_port, &sli, &sli_group);
+
+    /* The reset default for config retries is too short. Set it to 48ms, which
+       is what the Octeon SDK team is using. There is no documentation about
+       where they got the 48ms number */
+    int cfg_retry = 48 * 1000000 / (bdk_clock_get_rate(node, BDK_CLOCK_SCLK) >> 16);
+    if (cfg_retry >= 0x10000)
+        cfg_retry = 0xfffff;
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_CTL_STATUS(pcie_port),
+        c.cn83xx.cfg_rtry = cfg_retry);
+
+
+    /* Max Payload Size (PCIE*_CFG030[MPS]) */
+    /* Max Read Request Size (PCIE*_CFG030[MRRS]) */
+    /* Relaxed-order, no-snoop enables (PCIE*_CFG030[RO_EN,NS_EN] */
+    /* Error Message Enables (PCIE*_CFG030[CE_EN,NFE_EN,FE_EN,UR_EN]) */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG030(pcie_port),
+        c.s.mps = 1; /* Support 256 byte MPS */
+        c.s.mrrs = 0x5; /* Support 4KB MRRS */
+        c.s.ro_en = 1; /* Enable relaxed order processing. This will allow devices to affect read response ordering */
+        c.s.ns_en = 1; /* Enable no snoop processing. Not used */
+        c.s.ce_en = 1; /* Correctable error reporting enable. */
+        c.s.nfe_en = 1; /* Non-fatal error reporting enable. */
+        c.s.fe_en = 1; /* Fatal error reporting enable. */
+        c.s.ur_en = 1); /* Unsupported request reporting enable. */
+
+    /* Configure the PCIe slot number if specified */
+    int slot_num = bdk_config_get_int(BDK_CONFIG_PCIE_PHYSICAL_SLOT, node, pcie_port);
+    if (slot_num != -1)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG028(pcie_port),
+            c.s.si = 1); /* Slot Implemented*/
+        BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG033(pcie_port),
+            c.s.ps_num = slot_num);
+    }
+
+    /* Disable ECRC Generation as not all card support it. The OS can enable it
+       later if desired (PCIE*_CFG070[GE,CE]) */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG070(pcie_port),
+        c.s.ge = 0; /* ECRC generation disable. */
+        c.s.ce = 0); /* ECRC check disable. */
+
+    /* Access Enables (PCIE*_CFG001[MSAE,ME]) */
+        /* ME and MSAE should always be set. */
+    /* Interrupt Disable (PCIE*_CFG001[I_DIS]) */
+    /* System Error Message Enable (PCIE*_CFG001[SEE]) */
+        BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG001(pcie_port),
+        c.s.msae = 1; /* Memory space enable. */
+        c.s.me = 1; /* Bus master enable. */
+        c.s.i_dis = 1; /* INTx assertion disable. */
+        c.s.see = 1); /* SERR# enable */
+
+    /* Advanced Error Recovery Message Enables */
+    /* (PCIE*_CFG066,PCIE*_CFG067,PCIE*_CFG069) */
+    BDK_CSR_WRITE(node, BDK_PCIERCX_CFG066(pcie_port), 0);
+    /* Use BDK_PCIERCX_CFG067 hardware default */
+    BDK_CSR_WRITE(node, BDK_PCIERCX_CFG069(pcie_port), 0);
+
+
+    /* Active State Power Management (PCIE*_CFG032[ASLPC]) */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG032(pcie_port),
+        c.s.aslpc = 0); /* Active state Link PM control. */
+
+    /* Link Width Mode (PCIERCn_CFG452[LME]) - Set during bdk_pcie_rc_initialize_link() */
+    /* Primary Bus Number (PCIERCn_CFG006[PBNUM]) */
+    /* Use bus numbers as follows:
+        0 - 31: Reserved for internal ECAM
+        32 - 87: First PCIe on SLI
+        88 - 143: Second PCIe on SLI
+        144 - 199: Third PCIe on SLI
+        200 - 255: Fourth PCIe on SLI
+        Start bus = 32 + pcie * 56 */
+    const int BUSSES_PER_PCIE = 56;
+    int bus = 32 + sli_group * BUSSES_PER_PCIE;
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG006(pcie_port),
+        c.s.pbnum = 0;
+        c.s.sbnum = bus;
+        c.s.subbnum = bus + BUSSES_PER_PCIE - 1);
+
+    /* Memory-mapped I/O BAR (PCIERCn_CFG008) */
+    uint64_t mem_base = bdk_pcie_get_base_address(node, pcie_port, BDK_PCIE_MEM_NORMAL);
+    uint64_t mem_limit = mem_base + bdk_pcie_get_base_size(node, pcie_port, BDK_PCIE_MEM_NORMAL) - 1;
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG008(pcie_port),
+        c.s.mb_addr = mem_base >> 16;
+        c.s.ml_addr = mem_limit >> 16);
+
+    /* Prefetchable BAR (PCIERCn_CFG009,PCIERCn_CFG010,PCIERCn_CFG011) */
+    uint64_t prefetch_base = bdk_pcie_get_base_address(node, pcie_port, BDK_PCIE_MEM_PREFETCH);
+    uint64_t prefetch_limit = prefetch_base + bdk_pcie_get_base_size(node, pcie_port, BDK_PCIE_MEM_PREFETCH) - 1;
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG009(pcie_port),
+        c.s.lmem_base = prefetch_base >> 16;
+        c.s.lmem_limit = prefetch_limit >> 16);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG010(pcie_port),
+        c.s.umem_base = prefetch_base >> 32);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG011(pcie_port),
+        c.s.umem_limit = prefetch_limit >> 32);
+
+    /* System Error Interrupt Enables (PCIERCn_CFG035[SECEE,SEFEE,SENFEE]) */
+    /* PME Interrupt Enables (PCIERCn_CFG035[PMEIE]) */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG035(pcie_port),
+        c.s.secee = 1; /* System error on correctable error enable. */
+        c.s.sefee = 1; /* System error on fatal error enable. */
+        c.s.senfee = 1; /* System error on non-fatal error enable. */
+        c.s.pmeie = 1); /* PME interrupt enable. */
+
+    /* Advanced Error Recovery Interrupt Enables */
+    /* (PCIERCn_CFG075[CERE,NFERE,FERE]) */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG075(pcie_port),
+        c.s.cere = 1; /* Correctable error reporting enable. */
+        c.s.nfere = 1; /* Non-fatal error reporting enable. */
+        c.s.fere = 1); /* Fatal error reporting enable. */
+
+    /* Make sure the PEM agrees with GSERX about the speed its going to try */
+    BDK_CSR_INIT(pem_cfg, node, BDK_PEMX_CFG(pcie_port));
+    switch (pem_cfg.cn83xx.md)
+    {
+        case 0: /* Gen 1 */
+            /* Set the target link speed */
+            BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG040(pcie_port),
+                c.s.tls = 1);
+            break;
+        case 1: /* Gen 2 */
+            /* Set the target link speed */
+            BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG040(pcie_port),
+                c.s.tls = 2);
+            break;
+        case 2: /* Gen 3 */
+            /* Set the target link speed */
+            BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG040(pcie_port),
+                c.s.tls = 3);
+            break;
+        default:
+            bdk_error("N%d.PCIe%d: Unexpected rate of %d\n", node, pcie_port, pem_cfg.cn83xx.md);
+            break;
+    }
+
+    BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(pcie_port));
+    BDK_CSR_INIT(cfg452, node, BDK_PCIERCX_CFG452(pcie_port));
+    BDK_CSR_INIT(cfg031, node, BDK_PCIERCX_CFG031(pcie_port));
+    int lme = cfg452.s.lme;
+    int mlw = cfg031.s.mlw;
+
+    /* Link Width Mode (PCIERCn_CFG452[LME]) */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        lme = (pemx_cfg.cn88xx.lanes8) ? 0xf : 0x7;
+        mlw = (pemx_cfg.cn88xx.lanes8) ? 8 : 4;
+    }
+    /* CN83XX can support 8 lanes on QLM0+1 or QLM2+3. 4 lanes on DLM5+6 */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+    {
+        switch (pcie_port)
+        {
+            case 0: /* PEM0 on QLM0-1 */
+                lme = (pemx_cfg.cn83xx.lanes8) ? 0xf : 0x7;
+                mlw = (pemx_cfg.cn83xx.lanes8) ? 8 : 4;
+                break;
+            case 1: /* PEM1 on QLM1 */
+                lme = 0x7;
+                mlw = 4;
+                break;
+            case 2: /* PEM2 on QLM2-3 or DLM4 */
+            {
+                BDK_CSR_INIT(pemx_qlm, node, BDK_PEMX_QLM(pcie_port));
+                if (pemx_qlm.s.pem_bdlm) /* PEM2 is on DLM4 */
+                {
+                    lme = 0x3;
+                    mlw = 2;
+                }
+                else /* PEM2 is on QLM2 */
+                {
+                    lme = (pemx_cfg.cn83xx.lanes8) ? 0xf : 0x7;
+                    mlw = (pemx_cfg.cn83xx.lanes8) ? 8 : 4;
+                }
+                break;
+            }
+            case 3: /* PEM3 on QLM3 or DLM5-6 */
+            {
+                BDK_CSR_INIT(pemx_qlm, node, BDK_PEMX_QLM(pcie_port));
+                if (pemx_qlm.s.pem_bdlm) /* PEM3 is on DLM5-6 */
+                {
+                    lme = (pemx_cfg.cn83xx.lanes8) ? 0x7 : 0x3;
+                    mlw = (pemx_cfg.cn83xx.lanes8) ? 4 : 2;
+                }
+                else /* PEM3 is on QLM3 */
+                {
+                    lme = 0x7;
+                    mlw = 4;
+                }
+                break;
+            }
+        }
+    }
+    /* CN80XX only supports 1 lane on PEM0 */
+    if (cavium_is_altpkg(CAVIUM_CN81XX) && (pcie_port == 0))
+    {
+        lme = 1;
+        mlw = 1;
+    }
+
+    /* Allow override of hardware max link width  */
+    int max_width = bdk_config_get_int(BDK_CONFIG_PCIE_WIDTH, node, pcie_port);
+    switch (max_width)
+    {
+        case 1:
+            lme = 1;
+            mlw = 1;
+            break;
+        case 2:
+            lme = 3;
+            mlw = 2;
+            break;
+        case 4:
+            lme = 7;
+            mlw = 4;
+            break;
+        case 8:
+            lme = 0xf;
+            mlw = 8;
+            break;
+        case 16:
+            lme = 0x1f;
+            mlw = 16;
+            break;
+        default:
+            /* No change */
+            break;
+    }
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG452(pcie_port),
+        c.s.lme = lme);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG031(pcie_port),
+        c.s.mlw = mlw);
+
+    /* Errata PEM-25990 - Disable ASLPMS */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG031(pcie_port),
+        c.s.aslpms = 0);
+
+    /* Errata PEM-26189 - PEM EQ Preset Removal */
+    /* CFG554.PRV default changed from 16'h7ff to 16'h593. Should be
+       safe to apply to CN88XX, CN81XX, and CN83XX */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG554(pcie_port),
+        c.s.prv = bdk_config_get_int(BDK_CONFIG_PCIE_PRESET_REQUEST_VECTOR, node, pcie_port));
+
+    /* Errata PEM-26189 - Disable the 2ms timer on all chips */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG554(pcie_port),
+        c.s.p23td = 1);
+
+    /* Errata PEM-21178 - Change the CFG[089-092] LxUTP and LxDTP defaults.
+       Should be safe to apply to CN88XX, CN81XX, and CN83XX */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG089(pcie_port),
+        c.s.l0dtp = 0x7;
+        c.s.l0utp = 0x7;
+        c.cn83xx.l1dtp = 0x7;
+        c.s.l1utp = 0x7);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG090(pcie_port),
+        c.s.l2dtp = 0x7;
+        c.s.l2utp = 0x7;
+        c.s.l3dtp = 0x7;
+        c.s.l3utp = 0x7);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG091(pcie_port),
+        c.s.l4dtp = 0x7;
+        c.s.l4utp = 0x7;
+        c.s.l5dtp = 0x7;
+        c.s.l5utp = 0x7);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG092(pcie_port),
+        c.s.l6dtp = 0x7;
+        c.s.l6utp = 0x7;
+        c.s.l7dtp = 0x7;
+        c.s.l7utp = 0x7);
+
+    /* (ECAM-27114) PCIERC has incorrect device code */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG002(pcie_port),
+        c.s.sc = 0x4;
+        c.s.bcc = 0x6);
+
+    /* Errata PCIE-29440 - Atomic Egress ATOM_OP/ATOM_OP_EP not implemented
+       correctly */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG038(pcie_port),
+        c.s.atom_op =0x1;
+        c.s.atom_op_eb=0);
+
+    /* Errata PCIE-29566 PEM Link Hangs after going into L1 */
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG548(pcie_port),
+            c.s.grizdnc = 0x0);
+}
+
+/**
+ * Get the PCIe LTSSM state for the given port
+ *
+ * @param node      Node to query
+ * @param pcie_port PEM to query
+ *
+ * @return LTSSM state
+ */
+static int __bdk_pcie_rc_get_ltssm_state(bdk_node_t node, int pcie_port)
+{
+    /* LTSSM state is in debug select 0 */
+    BDK_CSR_WRITE(node, BDK_DTX_PEMX_SELX(pcie_port, 0), 0);
+    BDK_CSR_WRITE(node, BDK_DTX_PEMX_ENAX(pcie_port, 0), 0xfffffffffull);
+    /* Read the value */
+    uint64_t debug = BDK_CSR_READ(node, BDK_DTX_PEMX_DATX(pcie_port, 0));
+    /* Disable the PEM from driving OCLA signals */
+    BDK_CSR_WRITE(node, BDK_DTX_PEMX_ENAX(pcie_port, 0), 0);
+    if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+        return bdk_extract(debug, 0, 6); /* DBGSEL = 0x0, bits[5:0] */
+    else
+        return bdk_extract(debug, 3, 6); /* DBGSEL = 0x0, bits[8:3] */
+}
+
+/**
+ * Get the PCIe LTSSM state for the given port
+ *
+ * @param node      Node to query
+ * @param pcie_port PEM to query
+ *
+ * @return LTSSM state
+ */
+static const char *ltssm_string(int ltssm)
+{
+    switch (ltssm)
+    {
+        case 0x00: return "DETECT_QUIET";
+        case 0x01: return "DETECT_ACT";
+        case 0x02: return "POLL_ACTIVE";
+        case 0x03: return "POLL_COMPLIANCE";
+        case 0x04: return "POLL_CONFIG";
+        case 0x05: return "PRE_DETECT_QUIET";
+        case 0x06: return "DETECT_WAIT";
+        case 0x07: return "CFG_LINKWD_START";
+        case 0x08: return "CFG_LINKWD_ACEPT";
+        case 0x09: return "CFG_LANENUM_WAIT";
+        case 0x0A: return "CFG_LANENUM_ACEPT";
+        case 0x0B: return "CFG_COMPLETE";
+        case 0x0C: return "CFG_IDLE";
+        case 0x0D: return "RCVRY_LOCK";
+        case 0x0E: return "RCVRY_SPEED";
+        case 0x0F: return "RCVRY_RCVRCFG";
+        case 0x10: return "RCVRY_IDLE";
+        case 0x11: return "L0";
+        case 0x12: return "L0S";
+        case 0x13: return "L123_SEND_EIDLE";
+        case 0x14: return "L1_IDLE";
+        case 0x15: return "L2_IDLE";
+        case 0x16: return "L2_WAKE";
+        case 0x17: return "DISABLED_ENTRY";
+        case 0x18: return "DISABLED_IDLE";
+        case 0x19: return "DISABLED";
+        case 0x1A: return "LPBK_ENTRY";
+        case 0x1B: return "LPBK_ACTIVE";
+        case 0x1C: return "LPBK_EXIT";
+        case 0x1D: return "LPBK_EXIT_TIMEOUT";
+        case 0x1E: return "HOT_RESET_ENTRY";
+        case 0x1F: return "HOT_RESET";
+        case 0x20: return "RCVRY_EQ0";
+        case 0x21: return "RCVRY_EQ1";
+        case 0x22: return "RCVRY_EQ2";
+        case 0x23: return "RCVRY_EQ3";
+        default:   return "Unknown";
+    }
+}
+
+/**
+ * During PCIe link initialization we need to make config request to the attached
+ * device to verify its speed and width. These config access happen very early
+ * after the device is taken out of reset, so may fail for some amount of time.
+ * This function automatically retries these config accesses. The normal builtin
+ * hardware retry isn't enough for this very early access.
+ *
+ * @param node      Note to read from
+ * @param pcie_port PCIe port to read from
+ * @param bus       PCIe bus number
+ * @param dev       PCIe device
+ * @param func      PCIe function on the device
+ * @param reg       Register to read
+ *
+ * @return Config register value, or all ones on failure
+ */
+static uint32_t cfg_read32_retry(bdk_node_t node, int pcie_port, int bus, int dev, int func, int reg)
+{
+    /* Read the PCI config register until we get a valid value. Some cards
+       require time after link up to return data. Wait at most 3 seconds */
+    uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME) * 3;
+    uint32_t val;
+    do
+    {
+        /* Read PCI capability pointer */
+        val = bdk_pcie_config_read32(node, pcie_port, bus, dev, func, reg);
+        /* Check the read succeeded */
+        if (val != 0xffffffff)
+            return val;
+        /* Failed, wait a little and try again */
+        bdk_wait_usec(10000);
+    } while (bdk_clock_get_count(BDK_CLOCK_TIME) < timeout);
+
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Config read failed, can't communicate with device\n",
+        node, pcie_port);
+    return 0xffffffff;
+}
+
+/**
+ * Initialize a host mode PCIe link. This function assumes the PEM has already
+ * been taken out of reset and configure. It brings up the link and checks that
+ * the negotiated speed and width is correct for the configured PEM and the
+ * device plugged into it. Note that the return code will signal a retry needed
+ * for some link failures. The caller is responsible for PEM reset and retry.
+ *
+ * @param node      Node the PEM is on
+ * @param pcie_port PCIe port to initialize link on
+ *
+ * @return Zero on success
+ *         Negative on failures where retries are not needed
+ *         Positive if a retry is needed to fix a failure
+ */
+static int __bdk_pcie_rc_initialize_link(bdk_node_t node, int pcie_port)
+{
+    #define LTSSM_HISTORY_SIZE 64 /* Number of LTSSM transitions to record, must be a power of 2 */
+    uint8_t ltssm_history[LTSSM_HISTORY_SIZE];
+    int ltssm_history_loc;
+    bool do_retry_speed = false;
+
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Checking the PEM is out of reset\n", node, pcie_port);
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_PEMX_ON(pcie_port), pemoor, ==, 1, 100000))
+    {
+        printf("N%d.PCIe%d: PEM in reset, skipping.\n", node, pcie_port);
+        return -1;
+    }
+
+    /* Determine the maximum link speed and width */
+    BDK_CSR_INIT(pciercx_cfg031, node, BDK_PCIERCX_CFG031(pcie_port));
+    int max_gen = pciercx_cfg031.s.mls; /* Max speed of PEM from config (1-3) */
+    int max_width = pciercx_cfg031.s.mlw; /* Max lane width of PEM (1-8) */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Link supports up to %d lanes, speed gen%d\n",
+        node, pcie_port, max_width, max_gen);
+
+    /* Record starting LTSSM state for debug */
+    memset(ltssm_history, -1, sizeof(ltssm_history));
+    ltssm_history[0] = __bdk_pcie_rc_get_ltssm_state(node, pcie_port);
+    ltssm_history_loc = 0;
+
+    /* Bring up the link */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Enabling the link\n", node, pcie_port);
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_CTL_STATUS(pcie_port), c.cn83xx.lnk_enb = 1);
+
+    if (bdk_config_get_int(BDK_CONFIG_PCIE_SKIP_LINK_TRAIN, node, pcie_port)) {
+       BDK_TRACE(PCIE, "N%d.PCIe%d: Skipping link configuration\n", node, pcie_port);
+       return 0;
+    }
+
+retry_speed:
+    /* Clear RC Correctable Error Status Register */
+    BDK_CSR_WRITE(node, BDK_PCIERCX_CFG068(pcie_port), -1);
+
+    /* Wait for the link to come up and link training to be complete */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Waiting for link\n", node, pcie_port);
+
+    uint64_t clock_rate = bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME);
+    uint64_t hold_time = clock_rate / 5; /* 200ms */
+    uint64_t bounce_allow_time = clock_rate / 100; /* 10ms */
+    uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + clock_rate; /* Timeout = 1s */
+    uint64_t good_time = 0; /* Records when the link first went good */
+    BDK_CSR_DEFINE(pciercx_cfg032, BDK_PCIERCX_CFG032(pcie_port));
+    bool link_up;
+    bool is_loop_done;
+    do
+    {
+        /* Read link state */
+        pciercx_cfg032.u = BDK_CSR_READ(node, BDK_PCIERCX_CFG032(pcie_port));
+
+        /* Record LTSSM state for debug */
+        int ltssm_state = __bdk_pcie_rc_get_ltssm_state(node, pcie_port);
+        if (ltssm_history[ltssm_history_loc] != ltssm_state)
+        {
+            ltssm_history_loc = (ltssm_history_loc + 1) & (LTSSM_HISTORY_SIZE - 1);
+            ltssm_history[ltssm_history_loc] = ltssm_state;
+        }
+
+        /* Check if the link is up */
+        uint64_t current_time = bdk_clock_get_count(BDK_CLOCK_TIME);
+        link_up = (pciercx_cfg032.s.dlla && !pciercx_cfg032.s.lt);
+        if (link_up)
+        {
+            /* Is this the first link up? */
+            if (!good_time)
+            {
+                /* Mark the time when the link transitioned to good */
+                good_time = current_time;
+            }
+            else
+            {
+                /* Check for a link error */
+                BDK_CSR_INIT(cfg068, node, BDK_PCIERCX_CFG068(pcie_port));
+                if (cfg068.s.res)
+                {
+                    /* Ignore errors before we've been stable for bounce_allow_time */
+                    if (good_time + bounce_allow_time <= current_time)
+                    {
+                        BDK_TRACE(PCIE, "N%d.PCIe%d: Link errors after link up\n", node, pcie_port);
+                        return 1; /* Link error, signal a retry */
+                    }
+                    else
+                    {
+                        /* Clear RC Correctable Error Status Register */
+                        BDK_CSR_WRITE(node, BDK_PCIERCX_CFG068(pcie_port), -1);
+                        BDK_TRACE(PCIE, "N%d.PCIe%d: Ignored error during settling time\n", node, pcie_port);
+                    }
+                }
+            }
+        }
+        else if (good_time)
+        {
+            if (good_time + bounce_allow_time <= current_time)
+            {
+                /* We allow bounces for bounce_allow_time after the link is good.
+                   Once this time passes any bounce requires a retry */
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Link bounce detected\n", node, pcie_port);
+                return 1; /* Link bounce, signal a retry */
+            }
+            else
+            {
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Ignored bounce during settling time\n", node, pcie_port);
+            }
+        }
+
+        /* Determine if we've hit the timeout */
+        is_loop_done = (current_time >= timeout);
+        /* Determine if we've had a good link for the required hold time */
+        is_loop_done |= link_up && (good_time + hold_time <= current_time);
+    } while (!is_loop_done);
+
+    /* Trace the LTSSM state */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: LTSSM History\n", node, pcie_port);
+    for (int i = 0; i < LTSSM_HISTORY_SIZE; i++)
+    {
+        ltssm_history_loc = (ltssm_history_loc + 1) & (LTSSM_HISTORY_SIZE - 1);
+        if (ltssm_history[ltssm_history_loc] != 0xff)
+            BDK_TRACE(PCIE, "N%d.PCIe%d:     %s\n",
+                node, pcie_port, ltssm_string(ltssm_history[ltssm_history_loc]));
+    }
+
+    if (!link_up)
+    {
+        BDK_TRACE(PCIE, "N%d.PCIe%d: Link down, Data link layer %s(DLLA=%d), Link training %s(LT=%d), LTSSM %s\n",
+            node, pcie_port,
+            pciercx_cfg032.s.dlla ? "active" : "down", pciercx_cfg032.s.dlla,
+            pciercx_cfg032.s.lt ? "active" : "complete", pciercx_cfg032.s.lt,
+            ltssm_string(__bdk_pcie_rc_get_ltssm_state(node, pcie_port)));
+        return 1; /* Link down, signal a retry */
+    }
+
+    /* Report the negotiated link speed and width */
+    int neg_gen = pciercx_cfg032.s.ls; /* Current speed of PEM (1-3) */
+    int neg_width = pciercx_cfg032.s.nlw; /* Current lane width of PEM (1-8) */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Link negotiated %d lanes, speed gen%d\n",
+        node, pcie_port, neg_width, neg_gen);
+
+    /* Determine PCIe bus number the directly attached device uses */
+    BDK_CSR_INIT(pciercx_cfg006, node, BDK_PCIERCX_CFG006(pcie_port));
+    int bus = pciercx_cfg006.s.sbnum;
+
+    int dev_gen = 1; /* Device max speed (1-3) */
+    int dev_width = 1; /* Device max lane width (1-16) */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Reading device max speed and width\n",
+        node, pcie_port);
+
+    /* Read PCI capability pointer */
+    uint32_t cap = cfg_read32_retry(node, pcie_port, bus, 0, 0, 0x34);
+
+    /* Check if we were able to read capabilities pointer */
+    if (cap == 0xffffffff)
+        return 1; /* Signal retry needed */
+
+    /* Read device max speed and width */
+    int cap_next = cap & 0xff;
+    while (cap_next)
+    {
+        cap = cfg_read32_retry(node, pcie_port, bus, 0, 0, cap_next);
+        if (cap == 0xffffffff)
+            return 1; /* Signal retry needed */
+
+        /* Is this a PCIe capability (0x10)? */
+        if ((cap & 0xff) == 0x10)
+        {
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Found PCIe capability at offset 0x%x\n",
+                node, pcie_port, cap_next);
+            /* Offset 0xc contains the max link info */
+            cap = cfg_read32_retry(node, pcie_port, bus, 0, 0, cap_next + 0xc);
+            if (cap == 0xffffffff)
+                return 1; /* Signal retry needed */
+            dev_gen = cap & 0xf; /* Max speed of PEM from config (1-3) */
+            dev_width = (cap >> 4) & 0x3f; /* Max lane width of PEM (1-16) */
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Device supports %d lanes, speed gen%d\n",
+                node, pcie_port, dev_width, dev_gen);
+            break;
+        }
+        /* Move to next capability */
+        cap_next = (cap >> 8) & 0xff;
+    }
+
+    /* Desired link speed and width is either limited by the device or our PEM
+       configuration. Choose the most restrictive limit */
+    int desired_gen = (dev_gen < max_gen) ? dev_gen : max_gen;
+    int desired_width = (dev_width < max_width) ? dev_width : max_width;
+
+    /* We need a change if we don't match the desired speed or width. Note that
+       we allow better than expected in case the device lied about its
+       capabilities */
+    bool need_speed_change = (neg_gen < desired_gen);
+    bool need_lane_change = (neg_width < desired_width);
+
+    if (need_lane_change)
+    {
+        /* We didn't get the maximum number of lanes */
+        BDK_TRACE(PCIE, "N%d.PCIe%d: Link width (%d) less that supported (%d)\n",
+            node, pcie_port, neg_width, desired_width);
+        return 2; /* Link wrong width, signal a retry */
+    }
+    else if (need_speed_change)
+    {
+        if (do_retry_speed)
+        {
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Link speed (gen%d) less that supported (gen%d)\n",
+                node, pcie_port, neg_gen, desired_gen);
+            return 1; /* Link at width, but speed low. Request a retry */
+        }
+        else
+        {
+            /* We didn't get the maximum speed. Request a speed change */
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Link speed (gen%d) less that supported (gen%d), requesting a speed change\n",
+                node, pcie_port, neg_gen, desired_gen);
+            BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG515(pcie_port),
+                c.s.dsc = 1);
+            bdk_wait_usec(100000);
+            do_retry_speed = true;
+            goto retry_speed;
+        }
+    }
+    else
+    {
+        BDK_TRACE(PCIE, "N%d.PCIe%d: Link at best speed and width\n", node, pcie_port);
+        /* For gen3 links check if we are getting errors over the link */
+        if (neg_gen == 3)
+        {
+            /* Read RC Correctable Error Status Register */
+            BDK_CSR_INIT(cfg068, node, BDK_PCIERCX_CFG068(pcie_port));
+            if (cfg068.s.res)
+            {
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Link reporting error status\n", node, pcie_port);
+                return 1; /* Getting receiver errors, request a retry */
+            }
+        }
+        return 0; /* Link at correct speed and width */
+    }
+}
+
+/**
+ * Setup the SLI memory mapped regions to allow access to PCIe by the cores
+ * using addresses returned by bdk_pcie_get_base_address().
+ *
+ * @param node      Node to configure
+ * @param pcie_port PCIe port to configure
+ */
+static void __bdk_pcie_sli_initialize(bdk_node_t node, int pcie_port)
+{
+    int sli;
+    int sli_group;
+    __bdk_pcie_get_sli(node, pcie_port, &sli, &sli_group);
+
+    /* Setup store merge timer */
+    BDK_CSR_MODIFY(c, node, BDK_SLIX_S2M_CTL(sli),
+        c.s.max_word = 0;     /* Allow 16 words to combine */
+        c.s.timer = 50);      /* Wait up to 50 cycles for more data */
+
+    /* There are 256 regions per SLI. We need four regions per PCIe port to
+       support config, IO, normal, and prefetchable regions. The 256 regions
+       are shared across PCIe, so we need three groups of these (one group
+       for each PCIe). The setup is:
+       SLI bit[7:6]: PCIe port, relative to SLI (max of 4)
+       SLI bit[5:4]: Region. See bdk_pcie_mem_t enumeration
+       SLI bit[3:0]: Address extension from 32 bits to 36 bits
+       */
+    for (bdk_pcie_mem_t mem_region = BDK_PCIE_MEM_CONFIG; mem_region <= BDK_PCIE_MEM_IO; mem_region++)
+    {
+        /* Use top two bits for PCIe port, next two bits for memory region */
+        int sli_region = sli_group << 6;
+        /* Use next two bits for mem region type */
+        sli_region |= mem_region << 4;
+        /* Figure out the hardware setting for each region */
+        int ctype = 3;
+        int nmerge = 1;
+        int ordering = 0;
+        switch (mem_region)
+        {
+            case BDK_PCIE_MEM_CONFIG: /* Config space */
+                ctype = 1;      /* Config space */
+                nmerge = 1;     /* No merging allowed */
+                ordering = 0;   /* NO "relaxed ordering" or "no snoop" */
+                break;
+            case BDK_PCIE_MEM_NORMAL: /* Memory, not prefetchable */
+                ctype = 0;      /* Memory space */
+                nmerge = 1;     /* No merging allowed */
+                ordering = 0;   /* NO "relaxed ordering" or "no snoop" */
+                break;
+            case BDK_PCIE_MEM_PREFETCH: /* Memory, prefetchable */
+                ctype = 0;      /* Memory space */
+                nmerge = 0;     /* Merging allowed */
+                ordering = 1;   /* Yes "relaxed ordering" and "no snoop" */
+                break;
+            case BDK_PCIE_MEM_IO: /* IO */
+                ctype = 2;      /* I/O space */
+                nmerge = 1;     /* No merging allowed */
+                ordering = 0;   /* NO "relaxed ordering" or "no snoop" */
+                break;
+        }
+        /* Use the lower order bits to work as an address extension, allowing
+           each PCIe port to map a total of 36 bits (32bit each region, 16
+           regions) */
+        int epf = sli_group;
+        if (CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
+            BDK_CSR_INIT(lmac_const0,node,BDK_SLIX_LMAC_CONST0X(sli,pcie_port));
+            epf = lmac_const0.s.epf;
+        }
+        for (int r = sli_region; r < sli_region + 16; r++)
+        {
+            uint64_t address = 0;
+            /* Address only applies to memory space */
+            if (mem_region == BDK_PCIE_MEM_NORMAL)
+            {
+                /* Normal starts at bus address 0 */
+                address = r - sli_region;
+            } else if (mem_region == BDK_PCIE_MEM_PREFETCH)
+            {
+                /* Normal starts at bus address 0x10.0000.0000 */
+                address = r - sli_region + 16;
+            }
+            BDK_CSR_MODIFY(c, node, BDK_SLIX_S2M_REGX_ACC(sli, r),
+                c.s.ctype = ctype;
+                c.s.zero = 0;
+                c.cn83xx.epf = epf; /* Superimposed onto c.cn81xx.mac. EPF value works for both */
+                c.s.nmerge = nmerge;
+                c.s.wtype = ordering;
+                c.s.rtype = ordering;
+                c.s.ba = address);
+        }
+    }
+
+    /* Setup MAC control */
+    BDK_CSR_MODIFY(c, node, BDK_SLIX_M2S_MACX_CTL(sli, sli_group),
+        c.s.waitl_com = 1; /* Improves ordering in Ali flash testing */
+        c.s.ctlp_ro = 1;
+        c.s.ptlp_ro = 1;
+        c.s.wind_d = 1;
+        c.s.bar0_d = 1;
+        c.s.wait_com = (bdk_config_get_int(BDK_CONFIG_PCIE_ORDERING) == 1));
+}
+
+
+/**
+ * Perform a complete PCIe RC reset. This is documented in the HRM as issuing a
+ * fundamental reset
+ *
+ * @param node      Node to reset
+ * @param pcie_port PCIe port to reset
+ *
+ * @return Zero on success, negative on failure
+ */
+static int __bdk_pcie_rc_reset(bdk_node_t node, int pcie_port)
+{
+    /* Find which QLM/DLM is associated with this PCIe port */
+    int qlm = bdk_qlm_get_qlm_num(node, BDK_IF_PCIE, pcie_port, 0);
+    if (qlm < 0)
+        return -1;
+
+    /* Check if this PCIe port combines two QLM/DLM */
+    BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(pcie_port));
+    int is_dual = CAVIUM_IS_MODEL(CAVIUM_CN81XX) ? pemx_cfg.cn81xx.lanes4 : pemx_cfg.cn83xx.lanes8;
+
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Performing PCIe fundamental reset\n", node, pcie_port);
+
+    /* Host software may want to issue a fundamental reset to the PCIe bus.
+       Software should perform the following steps:
+       1.  Write PEM(0..1)_ON[PEMON] = 0. */
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_ON(pcie_port),
+        c.s.pemon = 0);
+    /* 2.  Write RST_SOFT_PRST(0..3)[SOFT_PRST] = 1.
+        - This reassertion of [SOFT_PRST] causes the chip to drive PERSTn_L
+            low (if RST_CTL(0..3)[RST_DRV] = 1). */
+    BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(pcie_port),
+        c.s.soft_prst = 1);
+    /* 3.  Read RST_SOFT_PRST(0..3). This ensures the PCIe bus is now in reset.
+        - Note that PCIERCn_CFGn registers cannot be accessed when
+        RST_SOFT_PRST(0..3)[SOFT_PRST] = 1. */
+    BDK_CSR_READ(node, BDK_RST_SOFT_PRSTX(pcie_port));
+    /* 4.  Write GSER(0..8)_PHY_CTL[PHY_RESET] = 1.
+        - This puts the PHY in reset. */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+        c.s.phy_reset = 1);
+    if (is_dual)
+        BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm + 1),
+            c.s.phy_reset = 1);
+    /* Wait 10 us before proceeding to step 5. */
+    bdk_wait_usec(10);
+    /* 5.  Write GSERx_PHY_CTL[PHY_RESET] = 0 */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+        c.s.phy_reset = 0);
+    if (is_dual)
+        BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm + 1),
+            c.s.phy_reset = 0);
+
+    /* Turn on PEM clocks */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+        BDK_CSR_MODIFY(c, node, BDK_PEMX_CLK_EN(pcie_port),
+            c.cn83xx.pceclk_gate = 0;
+            c.cn83xx.csclk_gate = 0);
+
+    /* 6.  Wait 2 ms or more before taking the PCIe port out of reset. */
+    bdk_wait_usec(2000);
+
+    /* To take PCIe port out of reset, perform the following steps: */
+    /* 1.  Write PEM(0..1)_ON[PEMON] = 1. */
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_ON(pcie_port),
+        c.s.pemon = 1);
+    /* 2.  Write RST_SOFT_PRST(0..3)[SOFT_PRST] = 0. */
+    /* 3.  After RST_CTL(0..3)[RST_DONE], perform any configuration as the
+       PCIe MAC has been reset. Set the PEM(0..1)_CTL_STATUS[LNK_ENB] = 1. */
+    /* These steps are executed when we bring the link up. See
+       bdk_pcie_rc_initialize() */
+    return 0;
+}
+
+/**
+ * Before PCIe link can be brought up a number of steps must be performed to
+ * reset the PEM, take the PEM out of reset, initialize the PEM, initialize
+ * RC config space, and initialize SLI. These steps must be performed every
+ * time the PEM is reset, which may be repeated if the PCIe link doesn't come
+ * up at the desired speed and width.
+ *
+ * @param node      Node to initialize
+ * @param pcie_port PCIe port to initialize
+ *
+ * @return Zero on success, negative on failure
+ */
+static int __bdk_pcie_rc_pre_link_init(bdk_node_t node, int pcie_port)
+{
+    /* Make sure the PEM and GSER do a full reset before starting PCIe */
+    if (__bdk_pcie_rc_reset(node, pcie_port))
+    {
+        bdk_error("N%d.PCIe%d: Reset failed.\n", node, pcie_port);
+        return -1;
+    }
+
+    /* Bring the PCIe out of reset */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Taking port out of reset\n", node, pcie_port);
+    BDK_CSR_WRITE(node, BDK_RST_SOFT_PRSTX(pcie_port), 0);
+
+    /* Check and make sure PCIe came out of reset. If it doesn't the board
+        probably hasn't wired the clocks up and the interface should be
+        skipped */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Waiting for reset to complete\n", node, pcie_port);
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_RST_CTLX(pcie_port), rst_done, ==, 1, 10000))
+    {
+        printf("N%d.PCIe%d: Stuck in reset, skipping.\n", node, pcie_port);
+        return -1;
+    }
+
+    /* Check BIST status */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Checking BIST\n", node, pcie_port);
+    BDK_CSR_INIT(pemx_bist_status, node, BDK_PEMX_BIST_STATUS(pcie_port));
+    if (pemx_bist_status.u)
+        bdk_warn("N%d.PCIe%d: BIST FAILED (0x%016llx)\n", node, pcie_port, pemx_bist_status.u);
+
+    /* Initialize the config space CSRs */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Setting up internal config space\n", node, pcie_port);
+    __bdk_pcie_rc_initialize_config_space(node, pcie_port);
+
+    /* Enable gen2 speed selection */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Enabling dynamic speed changes\n", node, pcie_port);
+    BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG515(pcie_port),
+        c.s.dsc = 1);
+
+    /* Setup the SLI windows to allow access to this PCIe from the core */
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Initializing SLI\n", node, pcie_port);
+    __bdk_pcie_sli_initialize(node, pcie_port);
+    return 0;
+}
+
+/**
+ * Initialize a PCIe port for use in host(RC) mode. It doesn't
+ * enumerate the bus.
+ *
+ * @param pcie_port PCIe port to initialize
+ *
+ * @return Zero on success
+ */
+int bdk_pcie_rc_initialize(bdk_node_t node, int pcie_port)
+{
+    const int MAX_RETRIES = 2; /* Total of 3 attempts: First + 2 retries */
+    int retry_count = 0;
+    int result= -1,i;
+    bdk_pemx_bar1_indexx_t bar1_idx;
+
+    /* Make sure we aren't trying to setup a target mode interface in host
+       mode. Sadly this bit is RAZ for CN88XX and CN81XX because the hardware
+       team removed it. So much for backward compatibility */
+    BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(pcie_port));
+    int host_mode = CAVIUM_IS_MODEL(CAVIUM_CN83XX) ? pemx_cfg.cn83xx.hostmd : 1;
+    if (!host_mode)
+    {
+        printf("N%d.PCIe%d: Port in endpoint mode.\n", node, pcie_port);
+        return -1;
+    }
+
+    while (retry_count <= MAX_RETRIES)
+    {
+        if (retry_count)
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Starting link retry %d\n", node, pcie_port, retry_count);
+        /* Perform init that must be done after PEM reset, but before link */
+        if (__bdk_pcie_rc_pre_link_init(node, pcie_port))
+            return -1;
+
+        if (retry_count == MAX_RETRIES)
+        {
+            BDK_CSR_INIT(pciercx_cfg031, node, BDK_PCIERCX_CFG031(pcie_port));
+           /* Drop speed to gen2 if link bouncing                  */
+           /* Result =-1  PEM in reset                             */
+           /* Result = 0: link speed and width ok no retry needed  */
+           /* Result = 1: Link errors or speed change needed       */
+           /* Result = 2: lane width error                         */
+           if ((pciercx_cfg031.s.mls == 3) && (result != 2))
+            {
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Dropping speed to gen2\n", node, pcie_port);
+                pciercx_cfg031.s.mls = 2;
+                BDK_CSR_WRITE(node, BDK_PCIERCX_CFG031(pcie_port), pciercx_cfg031.u);
+                /* Set the target link speed */
+                BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG040(pcie_port),
+                    c.s.tls = 2);
+            }
+        }
+        /* Bring the link up */
+        result = __bdk_pcie_rc_initialize_link(node, pcie_port);
+        if (result == 0)
+        {
+            BDK_TRACE(PCIE, "N%d.PCIe%d: Link does not need a retry\n", node, pcie_port);
+            break;
+        }
+        else if (result > 0)
+        {
+            if (retry_count >= MAX_RETRIES)
+            {
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Link requested a retry, but hit the max retries\n", node, pcie_port);
+                /* If the link is down, report failure */
+                BDK_CSR_INIT(pciercx_cfg032, node, BDK_PCIERCX_CFG032(pcie_port));
+                bool link_up = (pciercx_cfg032.s.dlla && !pciercx_cfg032.s.lt);
+                if (!link_up)
+                    result = -1;
+            }
+            else
+                BDK_TRACE(PCIE, "N%d.PCIe%d: Link requested a retry\n", node, pcie_port);
+        }
+        if (result < 0)
+        {
+            int ltssm_state = __bdk_pcie_rc_get_ltssm_state(node, pcie_port);
+            printf("N%d.PCIe%d: Link timeout, probably the slot is empty (LTSSM %s)\n",
+                node, pcie_port, ltssm_string(ltssm_state));
+            return -1;
+        }
+        retry_count++;
+    }
+
+    /* Errata PCIE-28816: Link retrain initiated at GEN1 can cause PCIE
+       link to hang. For Gen1 links we must disable equalization */
+    BDK_CSR_INIT(pciercx_cfg032, node, BDK_PCIERCX_CFG032(pcie_port));
+    if (pciercx_cfg032.s.ls == 1)
+    {
+        BDK_TRACE(PCIE, "N%d.PCIe%d: Disabling equalization for Gen1 link\n", node, pcie_port);
+        BDK_CSR_MODIFY(c, node, BDK_PCIERCX_CFG548(pcie_port),
+            c.s.ed = 1);
+    }
+
+    BDK_TRACE(PCIE, "N%d.PCIe%d: Setting up internal BARs\n", node, pcie_port);
+    /* Disable BAR0 */
+    BDK_CSR_WRITE(node, BDK_PEMX_P2N_BAR0_START(pcie_port), -1);
+    /* BAR1 Starting at address 0 */
+    BDK_CSR_WRITE(node, BDK_PEMX_P2N_BAR1_START(pcie_port), 0);
+    /* Set BAR2 to cover all memory starting at address 0 */
+    BDK_CSR_WRITE(node, BDK_PEMX_P2N_BAR2_START(pcie_port), 0);
+    /* Setup BAR attributes */
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_BAR_CTL(pcie_port),
+        c.cn83xx.bar1_siz = 1; /* 64MB BAR1 */
+        c.s.bar2_enb = 1; /* BAR2 is enabled */
+        c.s.bar2_cax = 0); /* Cache in L2 */
+
+    /* Allow devices that truncate the bus address to 32-bits to reach the GITS_TRANSLATER */
+    bar1_idx.u          = 0;
+    bar1_idx.s.addr_idx = bdk_numa_get_address(node, BDK_GITS_TRANSLATER) >> 22;
+    bar1_idx.s.addr_v   = 1;
+
+    BDK_CSR_WRITE(node, BDK_PEMX_BAR1_INDEXX(pcie_port, 0), bar1_idx.u);
+
+    /* The rest of the windows map linearly to match the BAR2 translation. */
+    for (i = 1; i < 16; i++)
+    {
+        bar1_idx.s.addr_idx = i;
+        BDK_CSR_WRITE(node, BDK_PEMX_BAR1_INDEXX(pcie_port, i), bar1_idx.u);
+    }
+
+    /* Display the link status */
+    printf("N%d.PCIe%d: Link active, %d lanes, speed gen%d\n",
+        node, pcie_port, pciercx_cfg032.s.nlw, pciercx_cfg032.s.ls);
+
+    return 0;
+
+}
+
+/**
+ * Return PCIe state
+ *
+ * @param pcie_port PCIe port to query
+ *
+ * @return True if port is up and running
+ */
+int bdk_pcie_is_running(bdk_node_t node, int pcie_port)
+{
+    BDK_CSR_INIT(pemx_on, node, BDK_PEMX_ON(pcie_port));
+    BDK_CSR_INIT(rst_soft_prstx, node, BDK_RST_SOFT_PRSTX(pcie_port));
+    BDK_CSR_INIT(pciercx_cfg032, node, BDK_PCIERCX_CFG032(pcie_port));
+
+    if (!pemx_on.s.pemon || rst_soft_prstx.s.soft_prst)
+        return 0;
+
+    return bdk_config_get_int(BDK_CONFIG_PCIE_SKIP_LINK_TRAIN, node, pcie_port) ||
+        (pciercx_cfg032.s.dlla && !pciercx_cfg032.s.lt);
+}
+
+/**
+ * Shutdown a PCIe port and put it in reset
+ *
+ * @param pcie_port PCIe port to shutdown
+ *
+ * @return Zero on success
+ */
+int bdk_pcie_rc_shutdown(bdk_node_t node, int pcie_port)
+{
+    /* Check that the controller is out of reset */
+    BDK_CSR_INIT(rst_ctlx, node, BDK_RST_CTLX(pcie_port));
+    if (!rst_ctlx.s.rst_done)
+        goto skip_idle_wait;
+
+    /* Check if link is up */
+    BDK_CSR_INIT(pciercx_cfg032, node, BDK_PCIERCX_CFG032(pcie_port));
+    if ((pciercx_cfg032.s.dlla == 0) || (pciercx_cfg032.s.lt == 1))
+        goto skip_idle_wait;
+#if 0 // FIXME
+    /* Wait for all pending operations to complete */
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_PEMX_CPL_LUT_VALID(pcie_port), tag, ==, 0, 2000))
+        printf("N%d.PCIe%d: Shutdown timeout\n", node, pcie_port);
+#endif
+skip_idle_wait:
+    /* Bring down the link */
+    BDK_CSR_MODIFY(c, node, BDK_PEMX_CTL_STATUS(pcie_port), c.cn83xx.lnk_enb = 0);
+    /* Force reset */
+    __bdk_pcie_rc_reset(node, pcie_port);
+    return 0;
+}
+
+/**
+ * @INTERNAL
+ * Build a PCIe config space request address for a device
+ *
+ * @param pcie_port PCIe port to access
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ *
+ * @return 64bit IO address
+ */
+uint64_t pcie_build_config_addr(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg)
+{
+    int num_pems = bdk_pcie_get_num_ports(node);
+    if (pcie_port < num_pems)
+    {
+        /* Errata (SLI-22555) ECAM to off-chip PCI misroutes address. Use
+           the SLI regions instead of ECAMs for config space access */
+        uint64_t address = bdk_pcie_get_base_address(node, pcie_port, BDK_PCIE_MEM_CONFIG);
+        /* Display the link status */
+        address += (uint64_t)bus << 24;   /* Bus is bits 31:24 */
+        address += dev << 19;   /* device+func is bits 23:16 */
+        address += fn << 16;
+        address += reg;         /* Offset is bits 11:0 */
+        return address;
+    }
+    else if (pcie_port >= 100)
+    {
+        bdk_device_t device;
+        memset(&device, 0, sizeof(device));
+        device.node = node;
+        device.ecam = pcie_port - 100;
+        device.bus = bus;
+        device.dev = dev;
+        device.func = fn;
+        return __bdk_ecam_build_address(&device, reg);
+    }
+    return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie.c
new file mode 100644
index 0000000..769550d
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-pcie.c
@@ -0,0 +1,221 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include "libbdk-hal/bdk-pcie.h"
+#include "libbdk-hal/bdk-utils.h"
+#include "libbdk-hal/if/bdk-if.h"
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(PCIE);
+
+/**
+ * Read 8bits from a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ *
+ * @return Result of the read
+ */
+uint8_t bdk_pcie_config_read8(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Read8(bus=%d, dev=%d, fn=%d, reg=0x%x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, address);
+    uint8_t result;
+    if (address)
+        result = bdk_read64_uint8(address);
+    else
+        result = 0xff;
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d:     Result=0x%02x\n", node, pcie_port, result);
+    return result;
+}
+
+
+/**
+ * Read 16bits from a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ *
+ * @return Result of the read
+ */
+uint16_t bdk_pcie_config_read16(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Read16(bus=%d, dev=%d, fn=%d, reg=0x%x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, address);
+    uint16_t result;
+    if (address)
+        result = bdk_le16_to_cpu(bdk_read64_uint16(address));
+    else
+        result = 0xffff;
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d:     Result=0x%04x\n", node, pcie_port, result);
+    return result;
+}
+
+
+/**
+ * Read 32bits from a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ *
+ * @return Result of the read
+ */
+uint32_t bdk_pcie_config_read32(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Read32(bus=%d, dev=%d, fn=%d, reg=0x%x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, address);
+
+    uint32_t result;
+    if (address)
+        result = bdk_le32_to_cpu(bdk_read64_uint32(address));
+    else
+        result = 0xffffffff;
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d:     Result=0x%08x\n", node, pcie_port, result);
+
+    /* Errata ECAM-22630: CN88XX pass 1.x, except pass 1.0, will return zero
+       for non-existent devices instead of ones. We look for this special case
+       for 32bit reads for reg=0 so we can scan device properly */
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X) && (reg == 0) && (result == 0))
+        result = 0xffffffff;
+
+    return result;
+}
+
+
+/**
+ * Write 8bits to a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ * @param val       Value to write
+ */
+void bdk_pcie_config_write8(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg, uint8_t val)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Write8(bus=%d, dev=%d, fn=%d, reg=0x%x, val=0x%02x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, val, address);
+    if (address)
+        bdk_write64_uint8(address, val);
+}
+
+
+/**
+ * Write 16bits to a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ * @param val       Value to write
+ */
+void bdk_pcie_config_write16(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg, uint16_t val)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Write16(bus=%d, dev=%d, fn=%d, reg=0x%x, val=0x%04x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, val, address);
+    if (address)
+        bdk_write64_uint16(address, bdk_cpu_to_le16(val));
+}
+
+
+/**
+ * Write 32bits to a Device's config space
+ *
+ * @param pcie_port PCIe port the device is on
+ * @param bus       Sub bus
+ * @param dev       Device ID
+ * @param fn        Device sub function
+ * @param reg       Register to access
+ * @param val       Value to write
+ */
+void bdk_pcie_config_write32(bdk_node_t node, int pcie_port, int bus, int dev, int fn, int reg, uint32_t val)
+{
+    uint64_t address = pcie_build_config_addr(node, pcie_port, bus, dev, fn, reg);
+    BDK_TRACE(PCIE_CONFIG, "N%d.PCIe%d: Config Write32(bus=%d, dev=%d, fn=%d, reg=0x%x, val=0x%08x, internal=0x%llx)\n",
+        node, pcie_port, bus, dev, fn, reg, val, address);
+    if (address)
+        bdk_write64_uint32(address, bdk_cpu_to_le32(val));
+}
+
+/**
+ * Read 64bits from PCIe using a memory transaction
+ *
+ * @param node      Node to read from
+ * @param pcie_port PCIe port to read
+ * @param address   PCIe address to read
+ *
+ * @return Result of the read
+ */
+uint64_t bdk_pcie_mem_read64(bdk_node_t node, int pcie_port, uint64_t address)
+{
+    uint64_t base_address = bdk_pcie_get_base_address(node, pcie_port, BDK_PCIE_MEM_NORMAL);
+    return bdk_read64_uint64(base_address + address);
+}
+
+/**
+ * Write 64bits to PCIe memory
+ *
+ * @param node      Node to write to
+ * @param pcie_port PCIe port to use
+ * @param address   Address to write
+ * @param data      Data to write
+ */
+void bdk_pcie_mem_write64(bdk_node_t node, int pcie_port, uint64_t address, uint64_t data)
+{
+    uint64_t base_address = bdk_pcie_get_base_address(node, pcie_port, BDK_PCIE_MEM_NORMAL);
+    bdk_write64_uint64(base_address + address, data);
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-qlm.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-qlm.c
new file mode 100644
index 0000000..f7d631f
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-qlm.c
@@ -0,0 +1,423 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-gsern.h"
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/qlm/bdk-qlm-common.h"
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(QLM);
+
+/**
+ * Convert a mode into a configuration variable string value
+ *
+ * @param mode   Mode to convert
+ *
+ * @return configuration value string
+ */
+const char *bdk_qlm_mode_to_cfg_str(bdk_qlm_modes_t mode)
+{
+#define MODE_CASE(m) case m: return #m+13
+    switch (mode)
+    {
+        MODE_CASE(BDK_QLM_MODE_DISABLED);
+        MODE_CASE(BDK_QLM_MODE_PCIE_1X1);
+        MODE_CASE(BDK_QLM_MODE_PCIE_2X1);
+        MODE_CASE(BDK_QLM_MODE_PCIE_1X2);
+        MODE_CASE(BDK_QLM_MODE_PCIE_1X4);
+        MODE_CASE(BDK_QLM_MODE_PCIE_1X8);
+        MODE_CASE(BDK_QLM_MODE_PCIE_1X16);
+
+        MODE_CASE(BDK_QLM_MODE_SATA_4X1);
+        MODE_CASE(BDK_QLM_MODE_SATA_2X1);
+
+        MODE_CASE(BDK_QLM_MODE_ILK);
+        MODE_CASE(BDK_QLM_MODE_SGMII_4X1);
+        MODE_CASE(BDK_QLM_MODE_SGMII_2X1);
+        MODE_CASE(BDK_QLM_MODE_SGMII_1X1);
+        MODE_CASE(BDK_QLM_MODE_XAUI_1X4);
+        MODE_CASE(BDK_QLM_MODE_RXAUI_2X2);
+        MODE_CASE(BDK_QLM_MODE_RXAUI_1X2);
+        MODE_CASE(BDK_QLM_MODE_OCI);
+        MODE_CASE(BDK_QLM_MODE_XFI_4X1);
+        MODE_CASE(BDK_QLM_MODE_XFI_2X1);
+        MODE_CASE(BDK_QLM_MODE_XFI_1X1);
+        MODE_CASE(BDK_QLM_MODE_XLAUI_1X4);
+        MODE_CASE(BDK_QLM_MODE_10G_KR_4X1);
+        MODE_CASE(BDK_QLM_MODE_10G_KR_2X1);
+        MODE_CASE(BDK_QLM_MODE_10G_KR_1X1);
+        MODE_CASE(BDK_QLM_MODE_40G_KR4_1X4);
+        MODE_CASE(BDK_QLM_MODE_QSGMII_4X1);
+        MODE_CASE(BDK_QLM_MODE_25G_4X1);
+        MODE_CASE(BDK_QLM_MODE_25G_2X1);
+        MODE_CASE(BDK_QLM_MODE_50G_2X2);
+        MODE_CASE(BDK_QLM_MODE_50G_1X2);
+        MODE_CASE(BDK_QLM_MODE_100G_1X4);
+        MODE_CASE(BDK_QLM_MODE_25G_KR_4X1);
+        MODE_CASE(BDK_QLM_MODE_25G_KR_2X1);
+        MODE_CASE(BDK_QLM_MODE_50G_KR_2X2);
+        MODE_CASE(BDK_QLM_MODE_50G_KR_1X2);
+        MODE_CASE(BDK_QLM_MODE_100G_KR4_1X4);
+        MODE_CASE(BDK_QLM_MODE_USXGMII_4X1);
+        MODE_CASE(BDK_QLM_MODE_USXGMII_2X1);
+
+        case BDK_QLM_MODE_LAST: break; /* fall through error */
+    }
+    return "INVALID_QLM_MODE_VALUE";
+}
+
+/**
+ * Convert a configuration variable value string into a mode
+ *
+ * @param val  Configuration variable value
+ *
+ * @return mode
+ */
+bdk_qlm_modes_t bdk_qlm_cfg_string_to_mode(const char *val)
+{
+    bdk_qlm_modes_t mode;
+
+    for (mode = 0; mode < BDK_QLM_MODE_LAST; mode++)
+    {
+        if (0 == strcmp(val, bdk_qlm_mode_to_cfg_str(mode)))
+        {
+            return mode;
+        }
+    }
+    return -1;
+}
+
+/**
+ * Convert a mode into a human understandable string
+ *
+ * @param mode   Mode to convert
+ *
+ * @return Easy to read string
+ */
+const char *bdk_qlm_mode_tostring(bdk_qlm_modes_t mode)
+{
+    const char *result = "Unknown, update bdk_qlm_mode_tostring()";
+    switch (mode)
+    {
+        case BDK_QLM_MODE_DISABLED:
+            result = "Disabled";
+            break;
+        case BDK_QLM_MODE_PCIE_1X1:
+            result = "1 PCIe, 1 lane";
+            break;
+        case BDK_QLM_MODE_PCIE_2X1:
+            result = "2 PCIe, 1 lane each";
+            break;
+        case BDK_QLM_MODE_PCIE_1X2:
+            result = "1 PCIe, 2 lanes";
+            break;
+        case BDK_QLM_MODE_PCIE_1X4:
+            result = "1 PCIe, 4 lanes";
+            break;
+        case BDK_QLM_MODE_PCIE_1X8:
+            result = "1 PCIe, 8 lanes";
+            break;
+        case BDK_QLM_MODE_PCIE_1X16:
+            result = "1 PCIe, 16 lanes";
+            break;
+
+        case BDK_QLM_MODE_SATA_4X1:
+            result = "4 SATA, one lane each";
+            break;
+        case BDK_QLM_MODE_SATA_2X1:
+            result = "2 SATA, one lane each";
+            break;
+
+        case BDK_QLM_MODE_ILK:
+            result = "Interlaken";
+            break;
+        case BDK_QLM_MODE_SGMII_4X1:
+            result = "4 SGMII, 1 lane each";
+            break;
+        case BDK_QLM_MODE_SGMII_2X1:
+            result = "2 SGMII, 1 lane each";
+            break;
+        case BDK_QLM_MODE_SGMII_1X1:
+            result = "1 SGMII, 1 lane";
+            break;
+        case BDK_QLM_MODE_XAUI_1X4:
+            result = "1 XAUI, 4 lanes";
+            break;
+        case BDK_QLM_MODE_RXAUI_2X2:
+            result = "2 RXAUI, 2 lanes each";
+            break;
+        case BDK_QLM_MODE_RXAUI_1X2:
+            result = "1 RXAUI, 2 lanes each";
+            break;
+        case BDK_QLM_MODE_OCI:
+            result = "Cavium Coherent Processor Interconnect";
+            break;
+        case BDK_QLM_MODE_XFI_4X1:
+            result = "4 XFI, 1 lane each";
+            break;
+        case BDK_QLM_MODE_XFI_2X1:
+            result = "2 XFI, 1 lane each";
+            break;
+        case BDK_QLM_MODE_XFI_1X1:
+            result = "1 XFI, 1 lane";
+            break;
+        case BDK_QLM_MODE_XLAUI_1X4:
+            result = "1 XLAUI, 4 lanes";
+            break;
+        case BDK_QLM_MODE_10G_KR_4X1:
+            result = "4 10GBASE-KR, 1 lane each";
+            break;
+        case BDK_QLM_MODE_10G_KR_2X1:
+            result = "2 10GBASE-KR, 1 lane each";
+            break;
+        case BDK_QLM_MODE_10G_KR_1X1:
+            result = "1 10GBASE-KR, 1 lane";
+            break;
+        case BDK_QLM_MODE_40G_KR4_1X4:
+            result = "1 40GBASE-KR4, 4 lanes";
+            break;
+        case BDK_QLM_MODE_QSGMII_4X1:
+            result = "4 QSGMII, 1 lane";
+            break;
+        case BDK_QLM_MODE_25G_4X1:
+            result = "4 25G, 1 lane each";
+            break;
+        case BDK_QLM_MODE_25G_2X1:
+            result = "2 25G, 1 lane each";
+            break;
+        case BDK_QLM_MODE_50G_2X2:
+            result = "2 50G, 2 lanes each";
+            break;
+        case BDK_QLM_MODE_50G_1X2:
+            result = "1 50G, 2 lanes";
+            break;
+        case BDK_QLM_MODE_100G_1X4:
+            result = "1 100G, 4 lanes";
+            break;
+        case BDK_QLM_MODE_25G_KR_4X1:
+            result = "4 25G, 1 lane each";
+            break;
+        case BDK_QLM_MODE_25G_KR_2X1:
+            result = "2 25G, 1 lane each";
+            break;
+        case BDK_QLM_MODE_50G_KR_2X2:
+            result = "2 50G, 2 lanes each";
+            break;
+        case BDK_QLM_MODE_50G_KR_1X2:
+            result = "1 50G, 2 lanes";
+            break;
+        case BDK_QLM_MODE_100G_KR4_1X4:
+            result = "1 100G, 4 lanes";
+            break;
+        case BDK_QLM_MODE_USXGMII_4X1:
+            result = "4 USXGMII, 1 lane each";
+            break;
+        case BDK_QLM_MODE_USXGMII_2X1:
+            result = "2 USXGMII, 1 lane each";
+            break;
+
+        case BDK_QLM_MODE_LAST:
+            break; /* fallthrough error */
+    }
+    return result;
+}
+
+int bdk_qlm_measure_clock(bdk_node_t node, int qlm)
+{
+    int ref_clock = __bdk_qlm_measure_refclock(node, qlm);
+    BDK_TRACE(QLM, "N%d.QLM%d: Ref clock %d Hz\n", node, qlm, ref_clock);
+
+    return ref_clock;
+}
+
+/**
+ * Set the QLM's clock source.
+ *
+ * @param node     Node to use in a Numa setup
+ * @param qlm      QLM to configure
+ * @param clk      Clock source for QLM
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_set_clock(bdk_node_t node, int qlm, bdk_qlm_clock_t clk)
+{
+    if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+    {
+        int sel;
+        int com1;
+        switch (clk)
+        {
+            case BDK_QLM_CLK_COMMON_0:
+                sel = 1;
+                com1 = 0;
+                break;
+            case BDK_QLM_CLK_COMMON_1:
+                sel = 1;
+                com1 = 1;
+                break;
+            case BDK_QLM_CLK_EXTERNAL:
+                sel = 0;
+                com1 = 0;
+                break;
+            default:
+                bdk_warn("Unrecognized clock mode %d for QLM%d on node %d.\n",
+                     clk, qlm, node);
+                return -1;
+        }
+
+        BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+            c.s.com_clk_sel = sel;
+            c.s.use_com1 = com1);
+    }
+    else
+    {
+        int cclksel;
+        switch (clk)
+        {
+            case BDK_QLM_CLK_COMMON_0:
+                cclksel = 0;
+                break;
+            case BDK_QLM_CLK_COMMON_1:
+                cclksel = 1;
+                break;
+            case BDK_QLM_CLK_COMMON_2:
+                cclksel = 2;
+                break;
+            case BDK_QLM_CLK_EXTERNAL:
+                cclksel = 3;
+                break;
+            default:
+                bdk_warn("Unrecognized clock mode %d for QLM%d on node %d.\n",
+                     clk, qlm, node);
+                return -1;
+        }
+        BDK_CSR_MODIFY(c, node, BDK_GSERNX_COMMON_REFCLK_BCFG(qlm),
+            c.s.pwdn = (clk == BDK_QLM_CLK_EXTERNAL) ? 0 : 1;
+            c.s.cclksel = cclksel);
+    }
+    return 0;
+}
+
+/**
+ * Display an eye diagram for the given QLM lane. The eye data can be in "eye", or
+ * captured during the call if "eye" is NULL.
+ *
+ * @param node     Node to use in numa setup
+ * @param qlm      QLM to use
+ * @param qlm_lane Which lane
+ * @param format   Display format. 0 = raw, 1 = Color ASCII
+ * @param eye      Eye data to display, or NULL if the data should be captured.
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_eye_display(bdk_node_t node, int qlm, int qlm_lane, int format, const bdk_qlm_eye_t *eye)
+{
+    int result;
+    int need_free = 0;
+    if (eye == NULL)
+    {
+        bdk_qlm_eye_t *eye_data = malloc(sizeof(bdk_qlm_eye_t));
+        if (eye_data == NULL)
+        {
+            bdk_error("Failed to allocate space for eye\n");
+            return -1;
+        }
+        if (bdk_qlm_eye_capture(node, qlm, qlm_lane, eye_data))
+            return -1;
+        eye = eye_data;
+    }
+
+    /* Calculate the max eye width */
+    int eye_area = 0;
+    int eye_width = 0;
+    for (int y = 0; y < eye->height; y++)
+    {
+        int width = 0;
+        for (int x = 0; x < eye->width; x++)
+        {
+            if (eye->data[y][x] == 0)
+            {
+                width++;
+                eye_area++;
+            }
+        }
+        if (width > eye_width)
+            eye_width = width;
+    }
+
+    /* Calculate the max eye height */
+    int eye_height = 0;
+    for (int x = 0; x < eye->width; x++)
+    {
+        int height = 0;
+        for (int y = 0; y < eye->height; y++)
+        {
+            if (eye->data[y][x] == 0)
+            {
+                height++;
+                eye_area++;
+            }
+        }
+        if (height > eye_height)
+            eye_height = height;
+    }
+
+    printf("\nEye Diagram for Node %d, QLM %d, Lane %d\n", node, qlm, qlm_lane);
+
+    if (format == 0) /* Raw */
+    {
+        for (int y = 0; y < eye->height; y++)
+        {
+            for (int x = 0; x < eye->width; x++)
+                printf("%u\t", eye->data[y][x]);
+            printf("\n");
+        }
+        result = 0;
+    }
+    else
+        result = -1;
+
+    if (need_free)
+        free((void*)eye);
+    return result;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-sata.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-sata.c
new file mode 100644
index 0000000..82e2d3d
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-sata.c
@@ -0,0 +1,1117 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <malloc.h>
+#include "libbdk-arch/bdk-csrs-sata.h"
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(SATA);
+
+/* Most all information used to create this code was gotten from this wiki
+   page: http://wiki.osdev.org/AHCI */
+
+/**
+ * Following code defines different kinds of FIS specified in Serial
+ *    ATA Revision 3.0.
+ */
+typedef enum
+{
+    FIS_TYPE_REG_H2D    = 0x27, /**< Register FIS - host to device */
+    FIS_TYPE_REG_D2H    = 0x34, /**< Register FIS - device to host */
+    FIS_TYPE_DMA_ACT    = 0x39, /**< DMA activate FIS - device to host */
+    FIS_TYPE_DMA_SETUP  = 0x41, /**< DMA setup FIS - bidirectional */
+    FIS_TYPE_DATA       = 0x46, /**< Data FIS - bidirectional */
+    FIS_TYPE_BIST       = 0x58, /**< BIST activate FIS - bidirectional */
+    FIS_TYPE_PIO_SETUP  = 0x5F, /**< PIO setup FIS - device to host */
+    FIS_TYPE_DEV_BITS   = 0xA1, /**< Set device bits FIS - device to host */
+} fis_type_t;
+
+/**
+ * A host to device register FIS is used by the host to send
+ * command or control to a device. As illustrated in the
+ * following data structure, it contains the IDE registers such
+ * as command, LBA, device, feature, count and control. An ATA
+ * command is constructed in this structure and issued to the
+ * device. All reserved fields in an FIS should be cleared to
+ * zero.
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_REG_H2D */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsv0:3;     /**< Reserved */
+        uint8_t c:1;        /**< 1: Command, 0: Control */
+        uint8_t command;    /**< Command register */
+        uint8_t featurel;   /**< Feature register, 7:0 */
+        // DWORD 1
+        uint8_t lba0;       /**< LBA low register, 7:0 */
+        uint8_t lba1;       /**< LBA mid register, 15:8 */
+        uint8_t lba2;       /**< LBA high register, 23:16 */
+        uint8_t device;     /**< Device register */
+        // DWORD 2
+        uint8_t lba3;       /**< LBA register, 31:24 */
+        uint8_t lba4;       /**< LBA register, 39:32 */
+        uint8_t lba5;       /**< LBA register, 47:40 */
+        uint8_t featureh;   /**< Feature register, 15:8 */
+        // DWORD 3
+        uint16_t count;     /**< Count register */
+        uint8_t icc;        /**< Isochronous command completion */
+        uint8_t control;    /**< Control register */
+        // DWORD 4
+        uint8_t rsv1[4];    /**< Reserved */
+#else
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_REG_H2D */
+        uint8_t c:1;        /**< 1: Command, 0: Control */
+        uint8_t rsv0:3;     /**< Reserved */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t command;    /**< Command register */
+        uint8_t featurel;   /**< Feature register, 7:0 */
+        // DWORD 1
+        uint8_t lba0;       /**< LBA low register, 7:0 */
+        uint8_t lba1;       /**< LBA mid register, 15:8 */
+        uint8_t lba2;       /**< LBA high register, 23:16 */
+        uint8_t device;     /**< Device register */
+        // DWORD 2
+        uint8_t lba3;       /**< LBA register, 31:24 */
+        uint8_t lba4;       /**< LBA register, 39:32 */
+        uint8_t lba5;       /**< LBA register, 47:40 */
+        uint8_t featureh;   /**< Feature register, 15:8 */
+        // DWORD 3
+        uint16_t count;     /**< Count register */
+        uint8_t icc;        /**< Isochronous command completion */
+        uint8_t control;    /**< Control register */
+        // DWORD 4
+        uint8_t rsv1[4];    /**< Reserved */
+#endif
+} fis_reg_h2d_t;
+
+/**
+ * A device to host register FIS is used by the device to notify
+ * the host that some ATA register has changed. It contains the
+ * updated task files such as status, error and other registers.
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DWORD 0
+        uint8_t fis_type;    /**< FIS_TYPE_REG_D2H */
+        uint8_t pmport:4;    /**< Port multiplier */
+        uint8_t rsv0:2;      /**< Reserved */
+        uint8_t i:1;         /**< Interrupt bit */
+        uint8_t rsv1:1;      /**< Reserved */
+        uint8_t status;      /**< Status register */
+        uint8_t error;       /**< Error register */
+        // DWORD 1
+        uint8_t lba0;        /**< LBA low register, 7:0 */
+        uint8_t lba1;        /**< LBA mid register, 15:8 */
+        uint8_t lba2;        /**< LBA high register, 23:16 */
+        uint8_t device;      /**< Device register */
+        // DWORD 2
+        uint8_t lba3;        /**< LBA register, 31:24 */
+        uint8_t lba4;        /**< LBA register, 39:32 */
+        uint8_t lba5;        /**< LBA register, 47:40 */
+        uint8_t rsv2;        /**< Reserved */
+        // DWORD 3
+        uint8_t countl;      /**< Count register, 7:0 */
+        uint8_t counth;      /**< Count register, 15:8 */
+        uint8_t rsv3[2];     /**< Reserved */
+        // DWORD 4
+        uint8_t rsv4[4];     /**< Reserved */
+#else
+        // DWORD 0
+        uint8_t fis_type;    /**< FIS_TYPE_REG_D2H */
+        uint8_t rsv1:1;      /**< Reserved */
+        uint8_t i:1;         /**< Interrupt bit */
+        uint8_t rsv0:2;      /**< Reserved */
+        uint8_t pmport:4;    /**< Port multiplier */
+        uint8_t status;      /**< Status register */
+        uint8_t error;       /**< Error register */
+        // DWORD 1
+        uint8_t lba0;        /**< LBA low register, 7:0 */
+        uint8_t lba1;        /**< LBA mid register, 15:8 */
+        uint8_t lba2;        /**< LBA high register, 23:16 */
+        uint8_t device;      /**< Device register */
+        // DWORD 2
+        uint8_t lba3;        /**< LBA register, 31:24 */
+        uint8_t lba4;        /**< LBA register, 39:32 */
+        uint8_t lba5;        /**< LBA register, 47:40 */
+        uint8_t rsv2;        /**< Reserved */
+        // DWORD 3
+        uint8_t countl;      /**< Count register, 7:0 */
+        uint8_t counth;      /**< Count register, 15:8 */
+        uint8_t rsv3[2];     /**< Reserved */
+        // DWORD 4
+        uint8_t rsv4[4];     /**< Reserved */
+#endif
+} fis_reg_d2h_t;
+
+/**
+ * This FIS is used by the host or device to send data payload.
+ * The data size can be varied.
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_DATA */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsv0:4;     /**< Reserved */
+        uint8_t rsv1[2];    /**< Reserved */
+        // DWORD 1 ~ N
+        uint32_t data[1];   /**< Payload */
+#else
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_DATA */
+        uint8_t rsv0:4;     /**< Reserved */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsv1[2];    /**< Reserved */
+        // DWORD 1 ~ N
+        uint32_t data[1];   /**< Payload */
+#endif
+} fis_data_t;
+
+/**
+ * This FIS is used by the device to tell the host that it's
+ * about to send or ready to receive a PIO data payload.
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_PIO_SETUP */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint8_t d:1;        /**< Data transfer direction, 1 - device to host */
+        uint8_t i:1;        /**< Interrupt bit */
+        uint8_t rsv1:1;
+        uint8_t status;     /**< Status register */
+        uint8_t error;      /**< Error register */
+        // DWORD 1
+        uint8_t lba0;       /**< LBA low register, 7:0 */
+        uint8_t lba1;       /**< LBA mid register, 15:8 */
+        uint8_t lba2;       /**< LBA high register, 23:16 */
+        uint8_t device;     /**< Device register */
+        // DWORD 2
+        uint8_t lba3;       /**< LBA register, 31:24 */
+        uint8_t lba4;       /**< LBA register, 39:32 */
+        uint8_t lba5;       /**< LBA register, 47:40 */
+        uint8_t rsv2;       /**< Reserved */
+        // DWORD 3
+        uint8_t countl;     /**< Count register, 7:0 */
+        uint8_t counth;     /**< Count register, 15:8 */
+        uint8_t rsv3;       /**< Reserved */
+        uint8_t e_status;   /**< New value of status register */
+        // DWORD 4
+        uint16_t tc;        /**< Transfer count */
+        uint8_t rsv4[2];    /**< Reserved */
+#else
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_PIO_SETUP */
+        uint8_t rsv1:1;
+        uint8_t i:1;        /**< Interrupt bit */
+        uint8_t d:1;        /**< Data transfer direction, 1 - device to host */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t status;     /**< Status register */
+        uint8_t error;      /**< Error register */
+        // DWORD 1
+        uint8_t lba0;       /**< LBA low register, 7:0 */
+        uint8_t lba1;       /**< LBA mid register, 15:8 */
+        uint8_t lba2;       /**< LBA high register, 23:16 */
+        uint8_t device;     /**< Device register */
+        // DWORD 2
+        uint8_t lba3;       /**< LBA register, 31:24 */
+        uint8_t lba4;       /**< LBA register, 39:32 */
+        uint8_t lba5;       /**< LBA register, 47:40 */
+        uint8_t rsv2;       /**< Reserved */
+        // DWORD 3
+        uint8_t countl;     /**< Count register, 7:0 */
+        uint8_t counth;     /**< Count register, 15:8 */
+        uint8_t rsv3;       /**< Reserved */
+        uint8_t e_status;   /**< New value of status register */
+        // DWORD 4
+        uint16_t tc;        /**< Transfer count */
+        uint8_t rsv4[2];    /**< Reserved */
+#endif
+} fis_pio_setup_t;
+
+/**
+ * DMA Setup ? Device to Host
+ */
+typedef struct __attribute__ ((__packed__))
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_DMA_SETUP */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint8_t d:1;        /**< Data transfer direction, 1 - device to host */
+        uint8_t i:1;        /**< Interrupt bit */
+        uint8_t a:1;        /**< Auto-activate. Specifies if DMA Activate FIS is needed */
+        uint8_t rsved[2];   /**< Reserved */
+        //DWORD 1&2
+        uint64_t DMAbufferID; /**< DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */
+        //DWORD 3
+        uint32_t rsvd;      /**< More reserved */
+        //DWORD 4
+        uint32_t DMAbufOffset; /**< Byte offset into buffer. First 2 bits must be 0 */
+        //DWORD 5
+        uint32_t TransferCount; /**< Number of bytes to transfer. Bit 0 must be 0 */
+        //DWORD 6
+        uint32_t resvd;     /**< Reserved */
+#else
+        // DWORD 0
+        uint8_t fis_type;   /**< FIS_TYPE_DMA_SETUP */
+        uint8_t a:1;        /**< Auto-activate. Specifies if DMA Activate FIS is needed */
+        uint8_t i:1;        /**< Interrupt bit */
+        uint8_t d:1;        /**< Data transfer direction, 1 - device to host */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint8_t pmport:4;   /**< Port multiplier */
+        uint8_t rsved[2];   /**< Reserved */
+        //DWORD 1&2
+        uint64_t DMAbufferID; /**< DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */
+        //DWORD 3
+        uint32_t rsvd;      /**< More reserved */
+        //DWORD 4
+        uint32_t DMAbufOffset; /**< Byte offset into buffer. First 2 bits must be 0 */
+        //DWORD 5
+        uint32_t TransferCount; /**< Number of bytes to transfer. Bit 0 must be 0 */
+        //DWORD 6
+        uint32_t resvd;     /**< Reserved */
+#endif
+} fis_dma_setup_t;
+
+typedef struct __attribute__ ((__packed__))
+{
+    uint8_t fis_type;   /**< FIS_TYPE_BIST */
+    uint8_t pmport:4;   /**< Port multiplier */
+    uint8_t rsv0:4;     /**< Reserved */
+    uint8_t v:1;        /**< Vendor Specific */
+    uint8_t r:1;        /**< Reserved */
+    uint8_t p:1;        /**< Primitive bit */
+    uint8_t f:1;        /**< Far end analog loopback */
+    uint8_t l:1;        /**< Far end retimed loopback */
+    uint8_t s:1;        /**< Scrambling bypass */
+    uint8_t a:1;        /**< Align bypass */
+    uint8_t t:1;        /**< Far end transmit only */
+    uint8_t rsv1;       /**< Reserved */
+    uint32_t data1;     /**< Only valid when "t" is set */
+    uint32_t data2;     /**< Only valid when "t" is set */
+} fis_bist_t;
+
+/**
+ * Received FIS Structure - AHCI rev 1.3 page 35
+ */
+typedef struct
+{
+        // 0x00
+        fis_dma_setup_t dsfis;      /**< DMA Setup FIS */
+        uint8_t         pad0[4];    /* Filler 0x1c - 0x1f */
+        // 0x20
+        fis_pio_setup_t psfis;      /**< PIO Setup FIS */
+        uint8_t         pad1[12];   /* Filler 0x34 - 0x3f */
+        // 0x40
+        fis_reg_d2h_t   rfis;       /**< Device to Host (D2H) Register FIS */
+        uint8_t         pad2[4];    /* Filler 0x54 - 0x57 */
+        // 0x58
+        uint8_t         sdbfis[8];  /**< Set Device Bit FIS */
+        // 0x60
+        uint8_t         ufis[64];   /**< Unknown FIS (up to 64 bytes) */
+        // 0xA0
+        uint8_t         rsv[0x100-0xA0]; /* Reserved */
+} hba_fis_t;
+
+/**
+ * Command header - AHCI rev 1.3 page 36
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        // DW0
+        uint8_t cfl:5;      /**< Command FIS length in DWORDS, 2 ~ 16 */
+        uint8_t a:1;        /**< ATAPI */
+        uint8_t w:1;        /**< Write, 1: H2D, 0: D2H */
+        uint8_t p:1;        /**< Prefetchable */
+        uint8_t r:1;        /**< Reset */
+        uint8_t b:1;        /**< BIST */
+        uint8_t c:1;        /**< Clear busy upon R_OK */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint8_t pmp:4;      /**< Port multiplier port */
+        uint16_t prdtl;     /**< Physical region descriptor table length in entries */
+        // DW1
+        uint32_t prdbc;     /**< Physical region descriptor byte count transferred */
+        // DW2, 3
+        uint64_t ctba;      /**< Command table descriptor base address. Must be 128 byte aligned */
+        // DW4 - 7
+        uint32_t rsv1[4];   /**< Reserved */
+#else
+        // DW0
+        uint8_t p:1;        /**< Prefetchable */
+        uint8_t w:1;        /**< Write, 1: H2D, 0: D2H */
+        uint8_t a:1;        /**< ATAPI */
+        uint8_t cfl:5;      /**< Command FIS length in DWORDS, 2 ~ 16 */
+        uint8_t pmp:4;      /**< Port multiplier port */
+        uint8_t c:1;        /**< Clear busy upon R_OK */
+        uint8_t b:1;        /**< BIST */
+        uint8_t r:1;        /**< Reset */
+        uint8_t rsv0:1;     /**< Reserved */
+        uint16_t prdtl;     /**< Physical region descriptor table length in entries */
+        // DW1
+        uint32_t prdbc;     /**< Physical region descriptor byte count transferred */
+        // DW2, 3
+        uint64_t ctba;      /**< Command table descriptor base address */
+        // DW4 - 7
+        uint32_t rsv1[4];   /**< Reserved */
+#endif
+} hba_cmd_header_t;
+
+/**
+ * Physical Region Descriptor Table Entry - AHCI rev 1.3 page 39
+ */
+typedef struct
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint64_t dba;       /**< Data base address. Must be 2 byte aligned */
+	uint32_t rsv0;      /**< Reserved */
+	uint32_t dbc:22;    /**< Byte count - 1, 4M max. Must be even number of bytes to transfer */
+	uint32_t rsv1:9;    /**< Reserved */
+	uint32_t i:1;       /**< Interrupt on completion */
+#else
+        uint64_t dba;       /**< Data base address */
+        uint32_t rsv0;      /**< Reserved */
+        uint32_t dbc;
+#endif
+} hba_prdt_entry_t;
+
+/**
+ * Command Table - AHCI rev 1.3 page 39
+ */
+typedef struct
+{
+	uint8_t cfis[64];   /**< Command FIS */
+	uint8_t acmd[16];   /**< ATAPI command, 12 or 16 bytes */
+        uint8_t rsv[48];    /**< Reserved */
+	hba_prdt_entry_t prdt_entry[1]; /**< Physical region descriptor table entries, 0 ~ 65535 */
+} hba_cmd_tbl_t;
+
+/**
+ * Return the number of SATA controllers on the chip
+ *
+ * @param node   Node to query
+ *
+ * @return Number of controllers, could be zero.
+ */
+int bdk_sata_get_controllers(bdk_node_t node)
+{
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+        return 16; /* 16 controllers on QLMs 2,3, 6-7 */
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+        return 6; /* 6 controllers on DLMs 4-6 */
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+        return 2; /* 2 controllers on DLM 2 */
+    else if (CAVIUM_IS_MODEL(CAVIUM_CN93XX))
+        return 4; /* 4 controllers on DLM 4-5 */
+    else
+        return 0;
+}
+
+static int __bdk_sata_is_initialized(bdk_node_t node, int controller)
+{
+    /* Make sure port is clocked before proceeding */
+    BDK_CSR_INIT(uctl_ctl, node, BDK_SATAX_UCTL_CTL(controller));
+    if (!uctl_ctl.s.a_clk_en || uctl_ctl.s.a_clkdiv_rst)
+        return 0;
+
+    /* See if the controller is started */
+    BDK_CSR_INIT(cmd, node, BDK_SATAX_UAHC_P0_CMD(controller));
+    return cmd.s.st;
+}
+
+/**
+ * Initialize a SATA controller and begin device detection
+ *
+ * @param node       Node to initialize
+ * @param controller Which controller to initialize
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_sata_initialize(bdk_node_t node, int controller)
+{
+    _Static_assert(sizeof(fis_reg_h2d_t) == 5 * 4, "Size of fis_reg_h2d_t wrong");
+    _Static_assert(sizeof(fis_reg_d2h_t)== 5 * 4, "Size of fis_reg_d2h_t wrong");
+    _Static_assert(sizeof(fis_data_t) == 2 * 4, "Size of fis_data_t wrong");
+    _Static_assert(sizeof(fis_pio_setup_t) == 5 * 4, "Size of fis_pio_setup_t wrong");
+    _Static_assert(sizeof(fis_dma_setup_t) == 7 * 4, "Size of fis_dma_setup_t wrong");
+    _Static_assert(sizeof(fis_bist_t) == 3 * 4, "Size of fis_bist_t wrong");
+    _Static_assert(sizeof(hba_fis_t) == 256, "Size of hba_fis_t wrong");
+    _Static_assert(sizeof(hba_cmd_header_t) == 8 * 4, "Size of hba_cmd_header_t wrong");
+    _Static_assert(sizeof(hba_prdt_entry_t) == 4 * 4, "Size of hba_prdt_entry_t wrong");
+    _Static_assert(sizeof(hba_cmd_tbl_t)== 128 + sizeof(hba_prdt_entry_t), "Size of hba_cmd_tbl_t wrong");
+
+    /* Make sure port is clocked before proceeding */
+    BDK_CSR_INIT(uctl_ctl, node, BDK_SATAX_UCTL_CTL(controller));
+    if (!uctl_ctl.s.a_clk_en || uctl_ctl.s.a_clkdiv_rst)
+    {
+        bdk_error("N%d.SATA%d: Not in SATA mode\n", node, controller);
+        return -1;
+    }
+
+    /* The following SATA setup is from the AHCI 1.3 spec, section
+       10.1.1, Firmware Specific Initialization. */
+    /* Early firmware setup was done in __bdk_qlm_set_sata(), we're not
+       starting the staggered spin-up process */
+
+    /* 1. Indicate that system software is AHCI aware by setting GHC.AE to '1'. */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_GHC(controller),
+        c.s.ae = 1);    /* AHCI enable */
+
+    /* 2. Ensure that PxCMD.ST = '0', PxCMD.CR = '0', PxCMD.FRE = '0',
+       PxCMD.FR = '0', and PxSCTL.DET = '0'. */
+    BDK_CSR_INIT(p0_cmd, node, BDK_SATAX_UAHC_P0_CMD(controller));
+    if (p0_cmd.s.st)
+        bdk_error("N%d.SATA%d: PxCMD[ST] is illegally set during init\n", node, controller);
+    if (p0_cmd.s.cr)
+        bdk_error("N%d.SATA%d: PxCMD[CR] is illegally set during init\n", node, controller);
+    if (p0_cmd.s.fre)
+        bdk_error("N%d.SATA%d: PxCMD[FRE] is illegally set during init\n", node, controller);
+    if (p0_cmd.s.fr)
+        bdk_error("N%d.SATA%d: PxCMD[FR] is illegally set during init\n", node, controller);
+    BDK_CSR_INIT(p0_sctl, node, BDK_SATAX_UAHC_P0_SCTL(controller));
+    if (p0_sctl.s.det)
+        bdk_error("N%d.SATA%d: PxSCTL[DET] is illegally set during init\n", node, controller);
+
+    /* 3. Allocate memory for the command list and the FIS receive area. Set
+       PxCLB and PxCLBU to the physical address of the allocated command list.
+       Set PxFB and PxFBU to the physical address of the allocated FIS receive
+       area. Then set PxCMD.FRE to '1'. */
+    /* Allocate area for commands */
+    uint64_t clb_pa = BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_CLB(controller));
+    if (clb_pa == 0)
+    {
+        void *clb = memalign(1024, sizeof(hba_cmd_header_t) * 32);
+        if (clb == NULL)
+        {
+            bdk_error("N%d.SATA%d: Failed to allocate command list\n", node, controller);
+            return -1;
+        }
+        memset(clb, 0, sizeof(hba_cmd_header_t) * 32);
+        BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_CLB(controller),
+            bdk_ptr_to_phys(clb));
+    }
+    /* Allocate area for FIS DMAs */
+    uint64_t fb_pa = BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_FB(controller));
+    if (fb_pa == 0)
+    {
+        hba_fis_t *fb = memalign(256, sizeof(hba_fis_t));
+        if (fb == NULL)
+        {
+            bdk_error("N%d.SATA%d: Failed to allocate FIS\n", node, controller);
+            return -1;
+        }
+        memset(fb, 0, sizeof(hba_fis_t));
+        BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_FB(controller),
+            bdk_ptr_to_phys(fb));
+    }
+
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_CMD(controller),
+        c.s.fre = 1);   /* FIS-receive enable */
+
+    /* 4. Initiate a spin up of the SATA drive attached to the port; i.e. set
+       PxCMD.SUD to '1'.*/
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_CMD(controller),
+        c.s.pod = 1;    /* Power on the device, only has affect if SATAX_UAHC_P0_CMD[CPD]=1 */
+        c.s.sud = 1);   /* Spin-up device */
+
+    /* 5. Wait for a positive indication that a device is attached to the port
+       (the maximum amount of time to wait for presence indication is specified
+       in the Serial ATA Revision 2.6 specification). This is done by polling
+       PxSSTS.DET. If PxSSTS.DET returns a value of 1h or 3h when read, then
+       system software shall continue to the next step, otherwise if the
+       polling process times out system software moves to the next implemented
+       port and returns to step 1. */
+    /* Waiting for device detection, up to 500ms. PxCMD[DET] must be 1 or 3 */
+    uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME) / 2;
+    BDK_CSR_INIT(p0_ssts, node, BDK_SATAX_UAHC_P0_SSTS(controller));
+    while ((p0_ssts.s.det != 1) && (p0_ssts.s.det != 3) &&
+           (bdk_clock_get_count(BDK_CLOCK_TIME) <= timeout))
+    {
+        p0_ssts.u = BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_SSTS(controller));
+        bdk_thread_yield();
+    }
+    if ((p0_ssts.s.det != 1) && (p0_ssts.s.det != 3))
+    {
+        bdk_error("N%d.SATA%d: PxSCTL[DET]=%d failed to detect a device\n", node, controller, p0_ssts.s.det);
+        goto fail;
+    }
+
+    /* 6. Clear the PxSERR register, by writing '1s' to each implemented bit
+       location. */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_SERR(controller), -1);
+
+    /* 7. Wait for indication that SATA drive is ready. This is determined via
+       an examination of PxTFD.STS. If PxTFD.STS.BSY, PxTFD.STS.DRQ, and
+       PxTFD.STS.ERR are all '0', prior to the maximum allowed time as
+       specified in the ATA/ATAPI-7 specification, the device is ready. */
+    /* Wait for the device to be ready. BSY(7), DRQ(3), and ERR(0) must be clear */
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_SATAX_UAHC_P0_TFD(controller), sts & 0x89, ==, 0, 5000000))
+    {
+        BDK_CSR_INIT(p0_tfd, node, BDK_SATAX_UAHC_P0_TFD(controller));
+        bdk_error("N%d.SATA%d: PxTFD[STS]=0x%x, Drive not ready\n", node, controller, p0_tfd.s.sts);
+        goto fail;
+    }
+
+    /* Enable AHCI command queuing */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_CCC_CTL(controller),
+        c.s.tv = 0;
+        c.s.en = 1);
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_CCC_PORTS(controller),
+        c.s.prt = 1);
+
+    /* Enable the FIS and clear any pending errors */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_FBS(controller),
+        c.s.dec = 1;
+        c.s.en = 1);
+
+    /* Disable all interrupts */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_IE(controller), 0);
+
+    /* Clear all status bits */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_IS(controller), -1);
+
+    /* Start the port controller */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_CMD(controller),
+        c.s.st = 1); /* Start the controller */
+    return 0;
+
+fail:
+    bdk_sata_shutdown(node, controller);
+    return -1;
+}
+
+/**
+ * Shutdown a SATA controller
+ *
+ * @param node       Node to access
+ * @param controller Controller to shutdown
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_sata_shutdown(bdk_node_t node, int controller)
+{
+    /* Remember the current speed limit and power management */
+    BDK_CSR_INIT(p0_sctl, node, BDK_SATAX_UAHC_P0_SCTL(controller));
+    /* Perform a HBA reset */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_GHC(controller),
+        c.s.hr = 1);
+    /* Wait for the reset to complete */
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_SATAX_UAHC_GBL_GHC(controller), hr, ==, 0, 100000))
+    {
+        bdk_error("N%d.SATA%d: Timeout waiting for HBA reset to complete\n", node, controller);
+        return -1;
+    }
+    /* Restore the speed limit and power management */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_SCTL(controller),
+        c.s.ipm = p0_sctl.s.ipm;
+        c.s.spd = p0_sctl.s.spd);
+    return 0;
+}
+
+/**
+ * Return the number of SATA ports connected to this AHCI controller
+ *
+ * @param node       Node to query
+ * @param controller SATA controller
+ *
+ * @return Number of ports. Zero if the controller doesn't connect to a QLM.
+ */
+int bdk_sata_get_ports(bdk_node_t node, int controller)
+{
+    BDK_CSR_INIT(ctl, node, BDK_SATAX_UAHC_GBL_CCC_CTL(controller));
+    return (ctl.s.en) ? 1 : 0;
+}
+
+/**
+ * Convert an IDE string into a C string with a NULL terminator
+ *
+ * @param buffer   Buffer for new string. Must be one longer than length
+ * @param original IDE string of identify command
+ * @param length   Length of the string in bytes
+ */
+static void get_ide_string(char *buffer, void *original, int length)
+{
+    /* Copy the IDE string 2 bytes at a time, swapping as we go */
+    uint16_t *newp = (uint16_t *)buffer;
+    uint16_t *oldp = (uint16_t *)original;
+    for (int i = 0; i < length / 2; i++)
+        newp[i] = bdk_swap16(oldp[i]);
+
+    /* Force a NULL terminator */
+    buffer[length] = 0;
+
+    /* Remove all trailing spaces */
+    while (buffer[length-1] == ' ')
+    {
+        buffer[length - 1] = 0;
+        length--;
+    }
+}
+
+static int issue_command(bdk_node_t node, int controller, int command, int is_write, uint64_t lba, void *buffer, int size)
+{
+    /* Pick a command slot to use */
+    int slot = 0;
+    hba_cmd_header_t *cmd_header = bdk_phys_to_ptr(BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_CLB(controller)));
+    cmd_header += slot;
+
+    /* Build a command table with the command to execute */
+    hba_cmd_tbl_t cmd_table BDK_CACHE_LINE_ALIGNED;
+    memset(&cmd_table, 0, sizeof(hba_cmd_tbl_t));
+    /* Where the data is */
+    cmd_table.prdt_entry[0].dba = bdk_cpu_to_le64(bdk_ptr_to_phys(buffer));
+    cmd_table.prdt_entry[0].dbc = bdk_cpu_to_le32(size - 1);
+
+    /* The actual command */
+    fis_reg_h2d_t *cmd_fis = (fis_reg_h2d_t *)cmd_table.cfis;
+    cmd_fis->fis_type = FIS_TYPE_REG_H2D;
+    cmd_fis->command = command;
+    cmd_fis->device = 1 << 6; /* LBA mode */
+    cmd_fis->c = 1; /* Write command register */
+    cmd_fis->lba0 = (lba >> 0) & 0xff;
+    cmd_fis->lba1 = (lba >> 8) & 0xff;
+    cmd_fis->lba2 = (lba >> 16) & 0xff;
+    cmd_fis->lba3 = (lba >> 24) & 0xff;
+    cmd_fis->lba4 = (lba >> 32) & 0xff;
+    cmd_fis->lba5 = (lba >> 40) & 0xff;
+    cmd_fis->count = bdk_cpu_to_le16(size / 512);
+
+    /* Setup the command header */
+    cmd_header->cfl = sizeof(fis_reg_h2d_t) / 4;
+    cmd_header->w = is_write;
+    cmd_header->prdtl = bdk_cpu_to_le16(1);
+    cmd_header->ctba = bdk_cpu_to_le64(bdk_ptr_to_phys(&cmd_table));
+
+    BDK_WMB;
+
+    /* Check that the slot is idle */
+    BDK_CSR_INIT(ci, node, BDK_SATAX_UAHC_P0_CI(controller));
+    if (ci.u & (1<<slot))
+    {
+        bdk_error("N%d.SATA%d: Command slot busy before submit\n", node, controller);
+        return -1;
+    }
+
+    /* Clear all status bits */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_IS(controller), -1);
+    BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_IS(controller));
+
+    /* Issue command */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_CI(controller), 1 << slot);
+
+    /* Wait for command accept */
+    const int TIMEOUT = 5000000; /* 5 seconds */
+    if (BDK_CSR_WAIT_FOR_FIELD(node,BDK_SATAX_UAHC_P0_CI(controller), ci & (1<<slot), ==, 0, TIMEOUT))
+    {
+        bdk_error("N%d.SATA%d: Command timeout\n", node, controller);
+        bdk_sata_shutdown(node, controller);
+        return -1;
+    }
+
+    /* Wait for completion */
+    if (BDK_CSR_WAIT_FOR_FIELD(node,BDK_SATAX_UAHC_P0_IS(controller), dhrs | c.s.pss | c.s.dss, !=, 0, TIMEOUT))
+    {
+        bdk_error("N%d.SATA%d: Command Response timeout\n", node, controller);
+        bdk_sata_shutdown(node, controller);
+        return -1;
+    }
+
+    /* Read status */
+    BDK_CSR_INIT(p_is, node, BDK_SATAX_UAHC_P0_IS(controller));
+    if (p_is.s.tfes)
+    {
+        bdk_error("N%d.SATA%d: Task-file error\n", node, controller);
+        bdk_sata_shutdown(node, controller);
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Identify the SATA device connected to a controller
+ *
+ * @param node       Node to query
+ * @param controller Controller to query
+ * @param port       Which SATA port on the controller, zero based
+ *
+ * @return Size of the disk in bytes
+ */
+uint64_t bdk_sata_identify(bdk_node_t node, int controller, int port)
+{
+    if (!__bdk_sata_is_initialized(node, controller))
+    {
+        if (bdk_sata_initialize(node, controller))
+            return 0;
+    }
+
+    const int TIMEOUT = 1000000; /* 1 seconds */
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_SATAX_UAHC_P0_SSTS(controller), ipm, !=, 0, TIMEOUT))
+    {
+        bdk_error("N%d.SATA%d: Device not present or communication not established\n", node, controller);
+        return 0;
+    }
+
+    /* Read the Serial ATA Status */
+    BDK_CSR_INIT(ssts, node, BDK_SATAX_UAHC_P0_SSTS(controller));
+
+    /* Check the link power state */
+    switch (ssts.s.ipm)
+    {
+        case 0: /* Device not present or communication not established */
+            BDK_TRACE(SATA, "N%d.SATA%d: Device not present or communication not established\n", node, controller);
+            return 0;
+        case 1: /* Interface in active state */
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in active state\n", node, controller);
+            break;
+        case 2: /* Interface in Partial power management state */
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in Partial power management state\n", node, controller);
+            return 0;
+        case 6: /* Interface in Slumber power management state */
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in Slumber power management state\n", node, controller);
+            return 0;
+        case 8: /* Interface in DevSleep power management state */
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in DevSleep power management state\n", node, controller);
+            return 0;
+        default:
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in unknown power state %d\n", node, controller, ssts.s.ipm);
+            return 0;
+    }
+
+    /* Check the link speed */
+    switch (ssts.s.spd)
+    {
+        case 0: /* Device not present or communication not established */
+            BDK_TRACE(SATA, "N%d.SATA%d: Device not present or communication not established\n", node, controller);
+            return 0;
+        case 1:
+        case 2:
+        case 3:
+            BDK_TRACE(SATA, "N%d.SATA%d: Speed Gen%d\n", node, controller, ssts.s.spd);
+            break;
+        default:
+            BDK_TRACE(SATA, "N%d.SATA%d: Interface in unknown speed %d\n", node, controller, ssts.s.spd);
+            return 0;
+    }
+
+    /* Check the device detection */
+    switch (ssts.s.det)
+    {
+        case 0: /* No device detected and Phy communication not established */
+            BDK_TRACE(SATA, "N%d.SATA%d: No device detected and Phy communication not established\n", node, controller);
+            return 0;
+        case 1: /* Device presence detected but Phy communication not established */
+            BDK_TRACE(SATA, "N%d.SATA%d: Device presence detected but Phy communication not established\n", node, controller);
+            return 0;
+        case 3: /* Device presence detected and Phy communication established */
+            BDK_TRACE(SATA, "N%d.SATA%d: Device presence detected and Phy communication established\n", node, controller);
+            break;
+        case 4: /* Phy in offline mode as a result of the interface being disabled or running in a BIST loopback mode */
+            BDK_TRACE(SATA, "N%d.SATA%d: Phy in offline mode\n", node, controller);
+            return 0;
+        default:
+            BDK_TRACE(SATA, "N%d.SATA%d: Device presence in unknown state %d\n", node, controller, ssts.s.det);
+            return 0;
+    }
+
+    /* Read the port signature to identify the device type */
+    BDK_CSR_INIT(sig, node, BDK_SATAX_UAHC_P0_SIG(controller));
+    switch (sig.s.sig)
+    {
+        case 0x00000101: /* SATA_SIG_ATA 0x00000101, SATA drive */
+            BDK_TRACE(SATA, "N%d.SATA%d: SATA drive\n", node, controller);
+            break;
+        case 0xEB140101: /* SATA_SIG_ATAPI 0xEB140101, SATAPI drive */
+            BDK_TRACE(SATA, "N%d.SATA%d: ATAPI drive, not supported by the BDK\n", node, controller);
+            return 0;
+        case 0xC33C0101: /* SATA_SIG_SEMB 0xC33C0101, Enclosure management bridge */
+            BDK_TRACE(SATA, "N%d.SATA%d: Enclosure management bridge, not supported by the BDK\n", node, controller);
+            return 0;
+        case 0x96690101: /* SATA_SIG_PM 0x96690101, Port multiplier */
+            BDK_TRACE(SATA, "N%d.SATA%d: Port multiplier, not supported by the BDK\n", node, controller);
+            return 0;
+        default: /* Just assume it is a drive */
+            BDK_TRACE(SATA, "N%d.SATA%d: Unknown signature 0x%08x, assuming a SATA drive\n", node, controller, sig.u);
+            break;
+    }
+
+    /* Send identify to the device */
+    const int ATA_CMD_IDENTIFY = 0xec;
+    char buffer[512];
+    if (issue_command(node, controller, ATA_CMD_IDENTIFY, 0, 0, buffer, sizeof(buffer)))
+        return 0;
+
+    /* Extract the data out of the IDENTIFY response */
+    uint16_t *ptr = (uint16_t *)buffer;
+    uint64_t sectors = bdk_le16_to_cpu(ptr[57]);
+    sectors += (uint32_t)bdk_le16_to_cpu(ptr[58]) << 16;
+    char serial[20  + 1];
+    get_ide_string(serial, ptr + 10, 20);
+    char firmware[8 + 1];
+    get_ide_string(firmware, ptr + 23, 8);
+    char model[40 + 1];
+    get_ide_string(model, ptr + 27, 40);
+
+    printf("N%d.SATA%d: Model=\"%s\", Firmware=\"%s\", Serial=\"%s\", Sectors=%lu, Link=Gen%d\n",
+        node, controller, model, firmware, serial, sectors, ssts.s.spd);
+
+    /* Return size in bytes */
+    return sectors * 512;
+}
+
+/**
+ * Read data from a SATA device
+ *
+ * @param node       Node the controller is on
+ * @param controller Which controller
+ * @param port       Which port on the controller, zero based
+ * @param lba        48 bit Block address to read
+ * @param sectors    Number of 512 bytes sectors to read
+ * @param buffer     Buffer to receive the data. Must be at least 512 * sectors in size
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_sata_read(bdk_node_t node, int controller, int port, uint64_t lba, int sectors, void *buffer)
+{
+    if (!__bdk_sata_is_initialized(node, controller))
+    {
+        if (bdk_sata_initialize(node, controller))
+            return -1;
+    }
+
+    const int ATA_READ_DMA = 0xc8;
+    if (issue_command(node, controller, ATA_READ_DMA, 0, lba, buffer, sectors * 512))
+        return -1;
+    return 0;
+}
+
+/**
+ * Write data to a SATA device
+ *
+ * @param node       Node the controller is on
+ * @param controller Which controller
+ * @param port       Which port on the controller, zero based
+ * @param lba        48 bit Block address to write
+ * @param sectors    Number of 512 bytes sectors to write
+ * @param buffer     Data buffer to write. Must be at least 512 * sectors in size
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_sata_write(bdk_node_t node, int controller, int port, uint64_t lba, int sectors, const void *buffer)
+{
+    if (!__bdk_sata_is_initialized(node, controller))
+    {
+        if (bdk_sata_initialize(node, controller))
+            return -1;
+    }
+
+    const int ATA_WRITE_DMA = 0xca;
+    if (issue_command(node, controller, ATA_WRITE_DMA, 1, lba, (void*)buffer, sectors * 512))
+        return -1;
+    return 0;
+}
+
+/**
+ * Enter one of the SATA pattern generation / loop testing modes
+ *
+ * @param node       Node to access
+ * @param controller SATA controller to access
+ * @param port       Which port on the controller
+ * @param mode       Test mode to enter
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_sata_bist_fis(bdk_node_t node, int controller, int port, bdk_sata_bist_fis_t mode)
+{
+    if (!__bdk_sata_is_initialized(node, controller))
+    {
+        if (bdk_sata_initialize(node, controller))
+            return -1;
+    }
+
+    /* Select the port we're doing BIST loopback on */
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_TESTR(controller),
+        c.s.psel = port);
+
+    /* Select the pattern */
+    int pattern;
+    switch (mode)
+    {
+        case BDK_SATA_BIST_SW_TX_ONLY_SSOP:
+        case BDK_SATA_BIST_SW_TX_ONLY_HTDP:
+        case BDK_SATA_BIST_SW_TX_ONLY_LTDP:
+        case BDK_SATA_BIST_SW_TX_ONLY_LFSCP:
+        case BDK_SATA_BIST_SW_TX_ONLY_COMP:
+        case BDK_SATA_BIST_SW_TX_ONLY_LBP:
+        case BDK_SATA_BIST_SW_TX_ONLY_MFTP:
+        case BDK_SATA_BIST_SW_TX_ONLY_HFTP:
+        case BDK_SATA_BIST_SW_TX_ONLY_LFTP:
+            pattern = mode - BDK_SATA_BIST_SW_TX_ONLY_SSOP;
+            break;
+        default:
+            pattern = 1; /* HTDP */
+            break;
+    }
+    BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_BISTCR(controller),
+        c.s.pattern = pattern);
+
+    /*
+        Note from the Synopsys SATA bist training video on pattern generation
+        without using BIST FIS.
+
+        1) Far-end Re-timed Loopback Responder Mode (Software Initiated)
+
+        In this mode the host controller receives the pattern and transmits
+        it back out. The setup of the mode is done by software, so no BIST FIS
+        frames are needed. After software sets it up, any pattern generator
+        should be able to send a pattern and get it back.
+
+        Setup:
+        1) Write SATAX_UAHC_GBL_BISTCR.ferlib = 1
+        2) Connect pattern generator
+        3) Pattern generator must send ALIGNs for PHY sync up
+        4) Pattern should be looped back out
+
+        2) Far-end Transmit Only Responder Mode (Software Initiated)
+
+        In this mode the host controller sends a transmit pattern and ignores
+        all input. This is useful for checking the TX eye diagram without an
+        external pattern generator.
+
+        Setup:
+        1) Write SATAX_UAHC_GBL_BISTCR.pattern to select the pattern.
+        2) Write SATAX_UAHC_GBL_BISTCR.txo = 1.
+        3) Host starts sending the requested BIST pattern.
+
+        BIST FIS Modes:
+        1) Far-end Analog Loopback (F=1)
+            Far end loops the received pattern back to transmit without retiming
+            the symbols. This is optional in the SATA 3.0 spec.
+        2) Far-end Retimed Loopback (L=1)
+            Far end loops the received pattern back to transmit after retiming
+            the symbols. This is mandatory in the SATA 3.0 spec.
+        3) Far-end Transmit Only (T=1, with other bits)
+            Far end transits a pattern and ignores its input. This is optional
+            in the SATA 3.0 spec.
+    */
+    if (mode == BDK_SATA_BIST_SW_RETIMED)
+    {
+        /* No FIS, just enter local retimed loopback */
+        BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_BISTCR(controller),
+            c.s.ferlib = 1);
+        BDK_TRACE(SATA, "N%d.SATA%d: Started Retimed loopback\n", node, controller);
+        return 0;
+    }
+    else if ((mode >= BDK_SATA_BIST_SW_TX_ONLY_SSOP) && (mode <= BDK_SATA_BIST_SW_TX_ONLY_LFTP))
+    {
+        /* No FIS, just enter local transit only */
+        BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_BISTCR(controller),
+            c.s.txo = 1);
+        BDK_TRACE(SATA, "N%d.SATA%d: Started tranmsit only\n", node, controller);
+        return 0;
+    }
+
+    /* Issue a BIST FIS command */
+
+    /* Pick a command slot to use */
+    int slot = 0;
+    hba_cmd_header_t *cmd_header = bdk_phys_to_ptr(BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_CLB(controller)));
+    cmd_header += slot;
+
+    /* Build a command table with the command to execute */
+    hba_cmd_tbl_t cmd_table BDK_CACHE_LINE_ALIGNED;
+    memset(&cmd_table, 0, sizeof(hba_cmd_tbl_t));
+
+    /* The actual BIST FIS command */
+    fis_bist_t *bist_fis = (fis_bist_t *)cmd_table.cfis;
+    bist_fis->fis_type = FIS_TYPE_BIST;
+    switch (mode)
+    {
+        case BDK_SATA_BIST_FIS_RETIMED: /* Send FIS to tell device to enter Retimed loopback */
+            bist_fis->l = 1;
+            break;
+        case BDK_SATA_BIST_FIS_ANALOG:  /* Send FIS to tell device to enter Analog loopback */
+            bist_fis->f = 1;
+            break;
+        case BDK_SATA_BIST_FIS_TX_ONLY: /* Send FIS to tell device to transit only */
+            bist_fis->t = 1;
+            break;
+        default:
+            bdk_error("Invalid SATA BIST FIS mode %d\n", mode);
+            return -1;
+    }
+
+    /* Setup the command header */
+    memset(cmd_header, 0, sizeof(hba_cmd_header_t));
+    cmd_header->cfl = sizeof(fis_bist_t) / 4;
+    cmd_header->b = 1;
+    cmd_header->ctba = bdk_ptr_to_phys(&cmd_table);
+
+    BDK_WMB;
+
+    /* Check that the slot is idle */
+    BDK_CSR_INIT(ci, node, BDK_SATAX_UAHC_P0_CI(controller));
+    if (ci.u & (1<<slot))
+    {
+        bdk_error("N%d.SATA%d: Command slot busy before submit\n", node, controller);
+        return -1;
+    }
+
+    /* Clear all status bits */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_IS(controller), -1);
+    BDK_CSR_READ(node, BDK_SATAX_UAHC_P0_IS(controller));
+
+    /* Issue command */
+    BDK_CSR_WRITE(node, BDK_SATAX_UAHC_P0_CI(controller), 1 << slot);
+    BDK_TRACE(SATA, "N%d.SATA%d: Sent BIST FIS\n", node, controller);
+
+    return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-twsi.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-twsi.c
deleted file mode 100644
index 4fbb78a..0000000
--- a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-twsi.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/***********************license start***********************************
-* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
-* reserved.
-*
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*
-*   * Redistributions of source code must retain the above copyright
-*     notice, this list of conditions and the following disclaimer.
-*
-*   * Redistributions in binary form must reproduce the above
-*     copyright notice, this list of conditions and the following
-*     disclaimer in the documentation and/or other materials provided
-*     with the distribution.
-*
-*   * Neither the name of Cavium Inc. nor the names of
-*     its contributors may be used to endorse or promote products
-*     derived from this software without specific prior written
-*     permission.
-*
-* This Software, including technical data, may be subject to U.S. export
-* control laws, including the U.S. Export Administration Act and its
-* associated regulations, and may be subject to export or import
-* regulations in other countries.
-*
-* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
-* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
-* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
-* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
-* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
-* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
-* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
-* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
-* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
-* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
-***********************license end**************************************/
-#include <bdk.h>
-#include "libbdk-arch/bdk-csrs-mio_tws.h"
-
-#define RECOVERY_UDELAY  5
-#define RECOVERY_CLK_CNT 9
-#define ARBLOST_UDELAY   5000 /* 5ms */
-
-/* This code is an optional part of the BDK. It is only linked in
-    if BDK_REQUIRE() needs it */
-BDK_REQUIRE_DEFINE(TWSI);
-
-/**
- * Initialize the TWSI blocks. This just sets the clock rate.
- * Many times stuff will work without calling this, but some
- * TWSI devices will fail. This is normally called automatically
- * in bdk-init-main.c.
- *
- * @return Zero on success, negative on failure
- */
-int bdk_twsix_initialize(bdk_node_t node)
-{
-    const int TWSI_BUS_FREQ = 100000;   /* 100 KHz */
-    const int TWSI_THP = 24;    	/* TCLK half period (default 24) */
-    const int io_clock_hz = bdk_clock_get_rate(node, BDK_CLOCK_SCLK);
-    int N_divider;
-    int M_divider;
-
-    /* Set the TWSI clock to a conservative TWSI_BUS_FREQ.  Compute the
-	clocks M divider based on the SCLK.
-	TWSI freq = (core freq) / (20 x (M+1) x (thp+1) x 2^N)
-	M = ((core freq) / (20 x (TWSI freq) x (thp+1) x 2^N)) - 1 */
-    for (N_divider = 0; N_divider < 8; N_divider++)
-    {
-	M_divider = (io_clock_hz / (20 * TWSI_BUS_FREQ * (TWSI_THP + 1) * (1 << N_divider))) - 1;
-	if (M_divider < 16)
-	    break;
-    }
-
-    BDK_CSR_DEFINE(sw_twsi, BDK_MIO_TWSX_SW_TWSI(bus));
-    sw_twsi.u = 0;
-    sw_twsi.s.v = 1;       /* Clear valid bit */
-    sw_twsi.s.op = 0x6;    /* See EOP field */
-    sw_twsi.s.r = 0;       /* Select CLKCTL when R = 0 */
-    sw_twsi.s.eop_ia = 3;  /* R=0 selects CLKCTL, R=1 selects STAT */
-    sw_twsi.s.data = ((M_divider & 0xf) << 3) | ((N_divider & 0x7) << 0);
-
-    int num_busses = 2;
-    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
-	num_busses = 6;
-
-    for (int bus = 0; bus < num_busses; bus++)
-    {
-	/* Only init non-slave ports */
-	BDK_CSR_INIT(state, node, BDK_MIO_TWSX_SW_TWSI(bus));
-	if (!state.s.slonly)
-	    BDK_CSR_WRITE(node, BDK_MIO_TWSX_SW_TWSI(bus), sw_twsi.u);
-    }
-    return 0;
-}
-
-/**
- * Do a twsi bus recovery in the case when the last transaction
- * on the bus has been left unfinished.
- *
- * @param twsi_id   which TWSI bus to use
- */
-static void bdk_twsix_recover_bus(bdk_node_t node, int twsi_id)
-{
-    /* read TWSX_INT */
-    BDK_CSR_INIT(twsx_int, node, BDK_MIO_TWSX_INT(twsi_id));
-
-    for (int i = 0; i < RECOVERY_CLK_CNT * 2; i++)
-    {
-	if (!twsx_int.s.scl_ovr)
-	{
-	    /* SCL shouldn't be low here */
-	    if (!twsx_int.s.scl)
-	    {
-		bdk_error("N%d.TWSI%d: SCL is stuck low\n", node, twsi_id);
-		return;
-	    }
-
-	    /* Break if SDA is high */
-	    if (twsx_int.s.sda)
-		break;
-	}
-
-	twsx_int.s.scl_ovr = !twsx_int.s.scl_ovr;
-	BDK_CSR_WRITE(node, BDK_MIO_TWSX_INT(twsi_id), twsx_int.u);
-	bdk_wait_usec(RECOVERY_UDELAY);
-    }
-
-    /*
-     * Generate STOP condition using the register overrides
-     * in order to move the higher level controller out of
-     * the bad state. This is a workaround for the TWSI hardware.
-     */
-    twsx_int.s.scl_ovr = 1;
-    twsx_int.s.sda_ovr = 1;
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_INT(twsi_id), twsx_int.u);
-    bdk_wait_usec(RECOVERY_UDELAY);
-    twsx_int.s.scl_ovr = 0;
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_INT(twsi_id), twsx_int.u);
-    bdk_wait_usec(RECOVERY_UDELAY);
-    twsx_int.s.sda_ovr = 0;
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_INT(twsi_id), twsx_int.u);
-}
-
-/**
- * Do a twsi read from a 7 bit device address using an (optional)
- * internal address. Up to 4 bytes can be read at a time.
- *
- * @param twsi_id   which TWSI bus to use
- * @param dev_addr  Device address (7 bit)
- * @param internal_addr
- *      	    Internal address.  Can be 0, 1 or 2 bytes in width
- * @param num_bytes Number of data bytes to read (1-4)
- * @param ia_width_bytes
- *      	    Internal address size in bytes (0, 1, or 2)
- *
- * @return Read data, or -1 on failure
- */
-int64_t bdk_twsix_read_ia(bdk_node_t node, int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes)
-{
-    bdk_mio_twsx_sw_twsi_t sw_twsi_val;
-    bdk_mio_twsx_sw_twsi_ext_t twsi_ext;
-    int retry_limit = 5;
-
-    if (num_bytes < 1 || num_bytes > 4 || ia_width_bytes < 0 || ia_width_bytes > 2)
-	return -1;
-retry:
-    twsi_ext.u = 0;
-    sw_twsi_val.u = 0;
-    sw_twsi_val.s.v = 1;
-    sw_twsi_val.s.r = 1;
-    sw_twsi_val.s.sovr = 1;
-    sw_twsi_val.s.size = num_bytes - 1;
-    sw_twsi_val.s.addr = dev_addr;
-
-    if (ia_width_bytes > 0)
-    {
-	sw_twsi_val.s.op = 1;
-	sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f;
-	sw_twsi_val.s.eop_ia = internal_addr & 0x7;
-	if (ia_width_bytes == 2)
-	{
-	    sw_twsi_val.s.eia = 1;
-	    twsi_ext.s.ia = internal_addr >> 8;
-	    BDK_CSR_WRITE(node, BDK_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u);
-	}
-    }
-
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u);
-    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_MIO_TWSX_SW_TWSI(twsi_id), v, ==, 0, 10000))
-    {
-	bdk_warn("N%d.TWSI%d: Timeout waiting for read to complete...start recovering process\n",
-		 node, twsi_id);
-	/* perform bus recovery */
-	bdk_twsix_recover_bus(node, twsi_id);
-	if (retry_limit-- > 0)
-	    goto retry;
-
-	bdk_error("N%d.TWSI%d: Timeout waiting for operation to complete\n", node, twsi_id);
-	return -1;
-    }
-    sw_twsi_val.u = BDK_CSR_READ(node, BDK_MIO_TWSX_SW_TWSI(twsi_id));
-    if (!sw_twsi_val.s.r)
-    {
-	/* Check the reason for the failure.  We may need to retry to handle multi-master
-	** configurations.
-	** Lost arbitration : 0x38, 0x68, 0xB0, 0x78
-	** Core busy as slave: 0x80, 0x88, 0xA0, 0xA8, 0xB8, 0xC0, 0xC8
-	*/
-	if (sw_twsi_val.s.data == 0x38
-	    || sw_twsi_val.s.data == 0x68
-	    || sw_twsi_val.s.data == 0xB0
-	    || sw_twsi_val.s.data == 0x78
-	    || sw_twsi_val.s.data == 0x80
-	    || sw_twsi_val.s.data == 0x88
-	    || sw_twsi_val.s.data == 0xA0
-	    || sw_twsi_val.s.data == 0xA8
-	    || sw_twsi_val.s.data == 0xB8
-	    || sw_twsi_val.s.data == 0xC8)
-	{
-	    /*
-	     * One of the arbitration lost conditions is recognized.
-	     * The TWSI hardware has switched to the slave mode and
-	     * expects the STOP condition on the bus.
-	     * Make a delay before next retry.
-	     */
-	    bdk_wait_usec(ARBLOST_UDELAY);
-	    if (retry_limit-- > 0)
-		goto retry;
-	}
-	/* For all other errors, return an error code */
-	return -1;
-    }
-
-    return (sw_twsi_val.s.data & (0xFFFFFFFF >> (32 - num_bytes*8)));
-}
-
-
-/**
- * Write 1-8 bytes to a TWSI device using an internal address.
- *
- * @param twsi_id   which TWSI interface to use
- * @param dev_addr  TWSI device address (7 bit only)
- * @param internal_addr
- *      	    TWSI internal address (0, 8, or 16 bits)
- * @param num_bytes Number of bytes to write (1-8)
- * @param ia_width_bytes
- *      	    internal address width, in bytes (0, 1, 2)
- * @param data      Data to write.  Data is written MSB first on the twsi bus, and
- *      	    only the lower num_bytes bytes of the argument are valid.  (If
- *      	    a 2 byte write is done, only the low 2 bytes of the argument is
- *      	    used.
- *
- * @return Zero on success, -1 on error
- */
-int bdk_twsix_write_ia(bdk_node_t node, int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t data)
-{
-    bdk_mio_twsx_sw_twsi_t sw_twsi_val;
-    bdk_mio_twsx_sw_twsi_ext_t twsi_ext;
-    int retry_limit = 5;
-    int to;
-
-    if (num_bytes < 1 || num_bytes > 8 || ia_width_bytes < 0 || ia_width_bytes > 2)
-	return -1;
-
-retry:
-    twsi_ext.u = 0;
-    sw_twsi_val.u = 0;
-    sw_twsi_val.s.v = 1;
-    sw_twsi_val.s.sovr = 1;
-    sw_twsi_val.s.size = num_bytes - 1;
-    sw_twsi_val.s.addr = dev_addr;
-    sw_twsi_val.s.data = 0xFFFFFFFF & data;
-
-    if (ia_width_bytes > 0)
-    {
-	sw_twsi_val.s.op = 1;
-	sw_twsi_val.s.ia = (internal_addr >> 3) & 0x1f;
-	sw_twsi_val.s.eop_ia = internal_addr & 0x7;
-    }
-    if (ia_width_bytes == 2)
-    {
-	sw_twsi_val.s.eia = 1;
-	twsi_ext.s.ia = internal_addr >> 8;
-    }
-    if (num_bytes > 4)
-	twsi_ext.s.data = data >> 32;
-
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_SW_TWSI_EXT(twsi_id), twsi_ext.u);
-    BDK_CSR_WRITE(node, BDK_MIO_TWSX_SW_TWSI(twsi_id), sw_twsi_val.u);
-    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_MIO_TWSX_SW_TWSI(twsi_id), v, ==, 0, 10000))
-    {
-	bdk_warn("N%d.TWSI%d: Timeout waiting for write to complete...start recovering process\n",
-		 node, twsi_id);
-	/* perform bus recovery */
-	bdk_twsix_recover_bus(node, twsi_id);
-	if (retry_limit-- > 0)
-	    goto retry;
-
-	// After retry but still not success, report error and return
-	bdk_error("N%d.TWSI%d: Timeout waiting for operation to complete\n", node, twsi_id);
-	return -1;
-    }
-
-    /* Poll until reads succeed, or polling times out */
-    to = 100;
-    while (to-- > 0)
-    {
-	if (bdk_twsix_read_ia(node, twsi_id, dev_addr, 0, 1, 0) >= 0)
-	    break;
-    }
-    if (to <= 0)
-	return -1;
-
-    return 0;
-}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c
new file mode 100644
index 0000000..eb2a85f
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-usb.c
@@ -0,0 +1,683 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-arch/bdk-csrs-gpio.h>
+#include <libbdk-arch/bdk-csrs-usbdrd.h>
+#include <libbdk-arch/bdk-csrs-usbh.h>
+#include <libbdk-hal/bdk-usb.h>
+#include <libbdk-hal/bdk-config.h>
+
+/* This code is an optional part of the BDK. It is only linked in
+    if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(USB);
+
+/**
+ * Write to DWC3 indirect debug control register
+ *
+ * @param node     Node to write to
+ * @param usb_port USB port to write to
+ * @param val      32bit value to write
+ */
+static void write_cr_dbg_cfg(bdk_node_t node, int usb_port, uint64_t val)
+{
+    if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+        BDK_CSR_WRITE(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
+    else
+        BDK_CSR_WRITE(node, BDK_USBHX_UCTL_PORTX_CR_DBG_CFG(usb_port, 0), val);
+}
+
+/**
+ * Poll the DWC3 internal status until the ACK bit matches a desired value. Return
+ * the final status.
+ *
+ * @param node     Node to query
+ * @param usb_port USB port to query
+ * @param desired_ack
+ *                 Desired ACK bit state
+ *
+ * @return Final status with ACK at correct state
+ */
+static bdk_usbdrdx_uctl_portx_cr_dbg_status_t get_cr_dbg_status(bdk_node_t node, int usb_port, int desired_ack)
+{
+    const int TIMEOUT = 1000000; /* 1 sec */
+    bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
+    if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+    {
+        if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
+        {
+            BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
+            status.u = -1;
+        }
+        else
+            status.u = BDK_CSR_READ(node, BDK_USBDRDX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
+    }
+    else
+    {
+        if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0), ack, ==, desired_ack, TIMEOUT))
+        {
+            BDK_TRACE(USB_XHCI, "N%d.USB%d: Timeout waiting for indirect ACK\n", node, usb_port);
+            status.u = -1;
+        }
+        else
+            status.u = BDK_CSR_READ(node, BDK_USBHX_UCTL_PORTX_CR_DBG_STATUS(usb_port, 0));
+    }
+    return status;
+}
+
+/**
+ * Perform an indirect read of an internal register inside the DWC3 usb block
+ *
+ * @param node     Node to read
+ * @param usb_port USB port to read
+ * @param addr     Indirect register address
+ *
+ * @return Value of the indirect register
+ */
+static uint32_t dwc3_uphy_indirect_read(bdk_node_t node, int usb_port, uint32_t addr)
+{
+    bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
+    bdk_usbdrdx_uctl_portx_cr_dbg_status_t status;
+
+    /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
+       the steps implemented by this function */
+
+    dbg_cfg.u = 0;
+    dbg_cfg.s.data_in = addr;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    dbg_cfg.s.cap_addr = 1;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    status = get_cr_dbg_status(node, usb_port, 1);
+    if (status.u == (uint64_t)-1)
+        return 0xffffffff;
+
+    write_cr_dbg_cfg(node, usb_port, 0);
+    get_cr_dbg_status(node, usb_port, 0);
+
+    dbg_cfg.u = 0;
+    dbg_cfg.s.read = 1;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    status = get_cr_dbg_status(node, usb_port, 1);
+
+    write_cr_dbg_cfg(node, usb_port, 0);
+    get_cr_dbg_status(node, usb_port, 0);
+
+    return status.s.data_out;
+}
+
+/**
+ * Perform an indirect write of an internal register inside the DWC3 usb block
+ *
+ * @param node     Node to write
+ * @param usb_port USB port to write
+ * @param addr     Indirect register address
+ * @param value    Value for write
+ */
+static void dwc3_uphy_indirect_write(bdk_node_t node, int usb_port, uint32_t addr, uint16_t value)
+{
+    bdk_usbdrdx_uctl_portx_cr_dbg_cfg_t dbg_cfg;
+
+    /* See the CSR description for USBHX_UCTL_PORTX_CR_DBG_CFG, which describes
+       the steps implemented by this function */
+
+    dbg_cfg.u = 0;
+    dbg_cfg.s.data_in = addr;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    dbg_cfg.s.cap_addr = 1;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    get_cr_dbg_status(node, usb_port, 1);
+
+    write_cr_dbg_cfg(node, usb_port, 0);
+    get_cr_dbg_status(node, usb_port, 0);
+
+    dbg_cfg.u = 0;
+    dbg_cfg.s.data_in = value;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    dbg_cfg.s.cap_data = 1;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    get_cr_dbg_status(node, usb_port, 1);
+
+    write_cr_dbg_cfg(node, usb_port, 0);
+    get_cr_dbg_status(node, usb_port, 0);
+
+    dbg_cfg.u = 0;
+    dbg_cfg.s.write = 1;
+    write_cr_dbg_cfg(node, usb_port, dbg_cfg.u);
+
+    get_cr_dbg_status(node, usb_port, 1);
+
+    write_cr_dbg_cfg(node, usb_port, 0);
+    get_cr_dbg_status(node, usb_port, 0);
+}
+
+/**
+ * Errata USB-29206 - The USB HS PLL in all 28nm devices has a
+ * design issue that may cause the VCO to lock up on
+ * initialization.  The Synopsys VCO is designed with an even
+ * number of stages and no kick-start circuit, which makes us
+ * believe that there is no question a latched up
+ * (non-oscillating) state is possible. The workaround is to
+ * check the PLL lock bit, which is just based on a counter and
+ * will not set if the VCO is not oscillating, and if it's not
+ * set do a power down/power up cycle on the PLL, which tests
+ * have proven is much more likely to guarantee the VCO will
+ * start oscillating.  Part of the problem appears to be that
+ * the normal init sequence holds the VCO in reset during the
+ * power up sequence, whereas the plain power up/down sequence
+ * does not, so the voltage changing may be helping the circuit
+ * to oscillate.
+ *
+ * @param node     Node to check
+ * @param usb_port USB port to check
+ *
+ * @return Zero on success, negative on failure
+ */
+static int dwc3_uphy_check_pll(bdk_node_t node, int usb_port)
+{
+    /* Internal indirect register that reports if the phy PLL has lock.  This will
+       be 1 if lock, 0 if no lock */
+    const int DWC3_INT_IND_PLL_LOCK_REG = 0x200b;
+    /* Internal indirect UPHY register that controls the power to the UPHY PLL */
+    const int DWC3_INT_IND_UPHY_PLL_PU = 0x2012;
+    /* Write enable bit for DWC3_INT_IND_PLL_POWER_CTL */
+    const int DWC3_INT_IND_UPHY_PLL_PU_WE = 0x20;
+    /* Power enable bit for DWC3_INT_IND_PLL_POWER_CTL */
+    const int DWC3_INT_IND_UPHY_PLL_PU_POWER_EN = 0x02;
+
+    uint32_t pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
+    int retry_count = 0;
+    while (!pll_locked)
+    {
+        if (retry_count >= 3)
+        {
+            bdk_error("N%d.USB%d: USB2 PLL failed to lock\n", node, usb_port);
+            return -1;
+        }
+
+        retry_count++;
+        BDK_TRACE(USB_XHCI, "N%d.USB%d: USB2 PLL didn't lock, retry %d\n", node, usb_port, retry_count);
+
+        /* Turn on write enable for PLL power control */
+        uint32_t pwr_val = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU);
+        pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_WE;
+        dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+
+        /* Power down the PLL */
+        pwr_val &= ~DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
+        dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+        bdk_wait_usec(1000);
+
+        /* Power on the PLL */
+        pwr_val |= DWC3_INT_IND_UPHY_PLL_PU_POWER_EN;
+        dwc3_uphy_indirect_write(node, usb_port, DWC3_INT_IND_UPHY_PLL_PU, pwr_val);
+        bdk_wait_usec(1000);
+
+        /* Check for PLL Lock again */
+        pll_locked = dwc3_uphy_indirect_read(node, usb_port, DWC3_INT_IND_PLL_LOCK_REG);
+    }
+    return 0;
+}
+
+/**
+ * Initialize the clocks for USB such that it is ready for a generic XHCI driver
+ *
+ * @param node       Node to init
+ * @param usb_port   Port to intialize
+ * @param clock_type Type of clock connected to the usb port
+ *
+ * @return Zero on success, negative on failure
+ */
+
+int bdk_usb_initialize(bdk_node_t node, int usb_port, bdk_usb_clock_t clock_type)
+{
+    int is_usbdrd = !CAVIUM_IS_MODEL(CAVIUM_CN88XX);
+
+    /* Perform the following steps to initiate a cold reset. */
+
+    /* 1.  Wait for all voltages to reach a stable state.  Ensure the
+        reference clock is up and stable.
+        a.  If 3.3V is up first, 0.85V must be soon after (within tens of ms). */
+
+    /* 2.  Wait for IOI reset to deassert. */
+
+    /* 3.  If Over Current indication and/or Port Power Control features
+        are desired, program the GPIO CSRs appropriately.
+        a.  For Over Current Indication, select a GPIO for the input and
+            program GPIO_USBH_CTL[SEL].
+        b. For Port Power Control, set one of
+            GPIO_BIT_CFG(0..19)[OUTPUT_SEL] = USBH_VBUS_CTRL. */
+
+    /* 4.  Assert all resets:
+        a.  UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 1
+        b. UAHC reset: USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 1
+        c. UCTL reset: USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 1 */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.uphy_rst = 1;
+            c.s.uahc_rst = 1;
+            c.s.uctl_rst = 1);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.uphy_rst = 1;
+            c.s.uahc_rst = 1;
+            c.s.uctl_rst = 1);
+    }
+
+    /* 5.  Configure the controller clock:
+        a.  Reset the clock dividers: USBDRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 1.
+        b. Select the controller clock frequency
+            USBDRD(0..1)_UCTL_CTL[H_CLKDIV] = desired value.
+            USBDRD(0..1)_UCTL_CTL[H_CLKDIV_EN] = 1 to enable the controller
+                clock.
+            Read USBDRD(0..1)_UCTL_CTL to ensure the values take effect.
+        c.  Deassert the controller clock divider reset: USB-
+            DRD(0..1)_UCTL_CTL[H_CLKDIV_RST] = 0. */
+    uint64_t sclk_rate = bdk_clock_get_rate(node, BDK_CLOCK_SCLK);
+    uint64_t divider = (sclk_rate + 300000000-1) / 300000000;
+    /*
+    ** According to HRM Rules are:
+    ** - clock must be below 300MHz
+    ** USB3 full-rate requires 150 MHz or better
+    ** USB3 requires 125 MHz
+    ** USB2 full rate requires 90 MHz
+    ** USB2 requires 62.5 MHz
+    */
+    if (divider <= 1)
+        divider = 0;
+    else if (divider <= 2)
+        divider = 1;
+    else if (divider <= 4)
+        divider = 2;
+    else if (divider <= 6)
+        divider = 3;
+    else if (divider <= 8)
+        divider = 4;
+    else if (divider <= 16)
+        divider = 5;
+    else if (divider <= 24)
+        divider = 6;
+    else
+        divider = 7;
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_rst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_sel = divider;
+            c.s.h_clk_en = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_rst = 0);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_rst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_sel = divider;
+            c.s.h_clk_en = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.h_clkdiv_rst = 0);
+    }
+    {
+        static bool printit[2] = {true,true};
+        if (printit[usb_port]) {
+            uint64_t fr_div;
+            if (divider < 5) fr_div = divider * 2;
+            else fr_div = 8 * (divider - 3);
+            uint64_t freq = (typeof(freq)) (sclk_rate / fr_div);
+            const char *token;
+            if (freq < 62500000ULL) token = "???Low";
+            else if (freq < 90000000ULL) token = "USB2";
+            else if (freq < 125000000ULL) token = "USB2 Full";
+            else if (freq < 150000000ULL) token = "USB3";
+            else token = "USB3 Full";
+            BDK_TRACE(USB_XHCI, "Freq %lld - %s\n",
+                   (unsigned long long)freq, token);
+            printit[usb_port] = false;
+        }
+    }
+
+    /* 6.  Configure the strap signals in USBDRD(0..1)_UCTL_CTL.
+        a.  Reference clock configuration (see Table 31.2): USB-
+            DRD(0..1)_UCTL_CTL[REF_CLK_FSEL, MPLL_MULTIPLIER,
+            REF_CLK_SEL, REF_CLK_DIV2].
+        b. Configure and enable spread-spectrum for SuperSpeed:
+            USBDRD(0..1)_UCTL_CTL[SSC_RANGE, SSC_EN, SSC_REF_CLK_SEL].
+        c. Enable USBDRD(0..1)_UCTL_CTL[REF_SSP_EN].
+        d. Configure PHY ports:
+            USBDRD(0..1)_UCTL_CTL[USB*_PORT_PERM_ATTACH, USB*_PORT_DISABLE]. */
+    if (is_usbdrd)
+    {
+        int ref_clk_src = 0;
+        int ref_clk_fsel = 0x27;
+        if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
+            if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
+                bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+                return -1;
+            }
+        }
+        else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
+            switch (clock_type)
+            {
+            default:
+                bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+                return -1;
+            case BDK_USB_CLOCK_SS_PAD_HS_PAD : ref_clk_src = 2; break;
+            case BDK_USB_CLOCK_SS_REF0_HS_REF0 : ref_clk_src = 0; break;  /* Superspeed and high speed use DLM/QLM ref clock 0 */
+            case BDK_USB_CLOCK_SS_REF1_HS_REF1 : ref_clk_src = 1; break;  /* Superspeed and high speed use DLM/QLM ref clock 1 */
+            case BDK_USB_CLOCK_SS_PAD_HS_PLL : ref_clk_src = 6; ref_clk_fsel = 0x7; break;    /* Superspeed uses PAD clock, high speed uses PLL ref clock */
+            case BDK_USB_CLOCK_SS_REF0_HS_PLL : ref_clk_src = 4; ref_clk_fsel = 0x7; break;    /* Superspeed uses DLM/QLM ref clock 0, high speed uses PLL ref clock */
+            case BDK_USB_CLOCK_SS_REF1_HS_PLL: ref_clk_src = 5; ref_clk_fsel =0x7; break;   /* Superspeed uses DLM/QLM ref clock 1, high speed uses PLL ref clock */
+            }
+        }
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.ref_clk_fsel = ref_clk_fsel;
+            c.s.mpll_multiplier = 0x19;
+            c.s.ref_clk_sel = ref_clk_src;
+            c.s.ref_clk_div2 = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.ssc_en = 1;
+            c.s.ssc_ref_clk_sel = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.ref_ssp_en = 1);
+    }
+    else
+    {
+        if (BDK_USB_CLOCK_SS_PAD_HS_PAD != clock_type) {
+            bdk_error("Node %d usb_port %d: usb clock type %d is invalid\n", node, usb_port, clock_type);
+            return -1;
+        }
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.ref_clk_fsel = 0x27;
+            c.s.mpll_multiplier = 0;
+            c.s.ref_clk_sel = 0;
+            c.s.ref_clk_div2 = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.ssc_en = 1;
+            c.s.ssc_ref_clk_sel = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.ref_ssp_en = 1);
+    }
+    /* Hardware default is for ports to be enabled and not perm attach. Don't
+        change it */
+
+    /* 7.  The PHY resets in lowest-power mode. Power up the per-port PHY
+       logic by enabling the following:
+        a.  USBDRD(0..1)_UCTL_CTL [HS_POWER_EN] if high-speed/full-speed/low-
+            speed functionality needed.
+        b. USBDRD(0..1)_UCTL_CTL [SS_POWER_EN] if SuperSpeed functionality
+            needed. */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.hs_power_en = 1;
+            c.s.ss_power_en = 1);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.hs_power_en = 1;
+            c.s.ss_power_en = 1);
+    }
+
+    /* 8.  Wait 10 controller-clock cycles from step 5. for controller clock
+        to start and async FIFO to properly reset. */
+    bdk_wait_usec(1);
+
+    /* 9.  Deassert UCTL and UAHC resets:
+        a.  USBDRD(0..1)_UCTL_CTL[UCTL_RST] = 0
+        b. USBDRD(0..1)_UCTL_CTL[UAHC_RST] = 0
+        c. [optional] For port-power control:
+        - Set one of GPIO_BIT_CFG(0..47)[PIN_SEL] =  USB0_VBUS_CTRLor USB1_VBUS_CTRL.
+        - Set USBDRD(0..1)_UCTL_HOST_CFG[PPC_EN] = 1 and USBDRD(0..1)_UCTL_HOST_CFG[PPC_ACTIVE_HIGH_EN] = 1.
+        - Wait for the external power management chip to power the VBUS.ional port-power control.
+        ]
+        d. You will have to wait 10 controller-clock cycles before accessing
+            any controller-clock-only registers. */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.uctl_rst = 0);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.uctl_rst = 0);
+    }
+    bdk_wait_usec(1);
+
+    int usb_gpio = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO, node, usb_port);
+    int usb_polarity = bdk_config_get_int(BDK_CONFIG_USB_PWR_GPIO_POLARITY, node, usb_port);
+    if (-1 != usb_gpio) {
+        int gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
+        if (CAVIUM_IS_MODEL(CAVIUM_CN88XX)) {
+            gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN88XX(usb_port);
+        }
+        else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) {
+            gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN81XX(usb_port);
+        }
+        else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) {
+            gsrc = BDK_GPIO_PIN_SEL_E_USBX_VBUS_CTRL_CN83XX(usb_port);}
+        else {
+            bdk_error("USB_VBUS_CTRL GPIO: unknown chip model\n");
+        }
+
+        BDK_CSR_MODIFY(c,node,BDK_GPIO_BIT_CFGX(usb_gpio),
+                       c.s.pin_sel = gsrc;
+                       c.s.pin_xor = (usb_polarity) ? 0 : 1;
+            );
+
+        if (is_usbdrd)
+        {
+            BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_HOST_CFG(usb_port),
+                           c.s.ppc_en = 1;
+                           c.s.ppc_active_high_en = 1);
+        }
+        else
+        {
+            BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_HOST_CFG(usb_port),
+                           c.s.ppc_en = 1;
+                           c.s.ppc_active_high_en = 1);
+        }
+        bdk_wait_usec(100000);
+    }
+
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.uahc_rst = 0);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.uahc_rst = 0);
+    }
+
+    bdk_wait_usec(100000);
+    bdk_wait_usec(1);
+
+    /* 10. Enable conditional coprocessor clock of UCTL by writing USB-
+        DRD(0..1)_UCTL_CTL[CSCLK_EN] = 1. */
+    if (is_usbdrd)
+    {
+        if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+        {
+            /* CN9XXX make coprocessor clock automatic */
+            BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+                c.cn83xx.csclk_en = 1);
+        }
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.csclk_en = 1);
+    }
+
+    /* 11. Set USBDRD(0..1)_UCTL_CTL[DRD_MODE] to 1 for device mode, 0 for
+        host mode. */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.drd_mode = 0);
+    }
+
+    /* 12. Soft reset the UPHY and UAHC logic via the UAHC controls:
+        a.  USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 1
+        b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 1
+        c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 1 */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+            c.s.physoftrst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+            c.s.physoftrst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+            c.s.coresoftreset = 1);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+            c.s.physoftrst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+            c.s.physoftrst = 1);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+            c.s.coresoftreset = 1);
+    }
+
+    /* 13. Program USBDRD(0..1)_UAHC_GCTL[PRTCAPDIR] to 0x2 for device mode
+        or 0x1 for host mode. */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+            c.s.prtcapdir = 1);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+            c.s.prtcapdir = 1);
+    }
+
+    /* 14. Wait 10us after step 13. for the PHY to complete its reset. */
+    bdk_wait_usec(10);
+
+    /* 15. Deassert UPHY reset: USBDRD(0..1)_UCTL_CTL[UPHY_RST] = 0. */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UCTL_CTL(usb_port),
+            c.s.uphy_rst = 0);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UCTL_CTL(usb_port),
+            c.s.uphy_rst = 0);
+    }
+
+    /* 16. Wait for at least 45us after step 15. for UPHY to output
+        stable PHYCLOCK. */
+    bdk_wait_usec(45);
+
+    /* Workround Errata USB-29206 */
+    if (dwc3_uphy_check_pll(node, usb_port))
+        return -1;
+
+    /* 17. Initialize any other strap signals necessary and make sure they
+       propagate by reading back the last register written.
+        a.  UCTL
+            USBDRD(0..1)_UCTL_PORT0_CFG_*[*_TUNE]
+            USBDRD(0..1)_UCTL_PORT0_CFG_*[PCS_*]
+            USBDRD(0..1)_UCTL_PORT0_CFG_*[LANE0_TX_TERM_OFFSET]
+            USBDRD(0..1)_UCTL_PORT0_CFG_*[TX_VBOOST_LVL]
+            USBDRD(0..1)_UCTL__PORT0_CFG_*[LOS_BIAS]
+            USBDRD(0..1)_UCTL_HOST_CFG
+            USBDRD(0..1)_UCTL_SHIM_CFG
+        b.  UAHC: only the following UAHC registers are accessible during
+            CoreSoftReset.
+            USBDRD(0..1)_UAHC_GCTL
+            USBDRD(0..1)_UAHC_GUCTL
+            USBDRD(0..1)_UAHC_GSTS
+            USBDRD(0..1)_UAHC_GUID
+            USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)
+            USBDRD(0..1)_UAHC_GUSB3PIPECTL(0) */
+
+    /* 18. Release soft reset the UPHY and UAHC logic via the UAHC controls:
+        a.  USBDRD(0..1)_UAHC_GUSB2PHYCFG(0)[PHYSOFTRST] = 0
+        b. USBDRD(0..1)_UAHC_GUSB3PIPECTL(0)[PHYSOFTRST] = 0
+        c. USBDRD(0..1)_UAHC_GCTL[CORESOFTRESET] = 0 */
+    if (is_usbdrd)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+            c.s.physoftrst = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+            c.s.physoftrst = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBDRDX_UAHC_GCTL(usb_port),
+            c.s.coresoftreset = 0);
+    }
+    else
+    {
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB2PHYCFGX(usb_port, 0),
+            c.s.physoftrst = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GUSB3PIPECTLX(usb_port, 0),
+            c.s.physoftrst = 0);
+        BDK_CSR_MODIFY(c, node, BDK_USBHX_UAHC_GCTL(usb_port),
+            c.s.coresoftreset = 0);
+    }
+
+    /* 19. Configure the remaining UAHC_G* registers as needed, including
+       any that were not configured in step 17.-b. */
+
+    /* 20. Initialize the USB controller:
+        a. To initialize the UAHC as a USB host controller, the application
+            should perform the steps described in the xHCI specification
+            (UAHC_X* registers). The xHCI sequence starts with poll for a 0 in
+            USBDRD(0..1)_UAHC_USBSTS[CNR].
+        b. To initialize the UAHC as a device, the application should TBD. The
+            device initiation sequence starts with a device soft reset by
+            setting USBDRD(0..1)_UAHC_DCTL[CSFTRST] = 1 and wait for a read
+            operation to return 0. */
+    return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c b/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c
new file mode 100644
index 0000000..a8e65f0
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/device/bdk-device.c
@@ -0,0 +1,721 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <string.h>
+#include "libbdk-arch/bdk-csrs-ap.h"
+#include "libbdk-arch/bdk-csrs-pccpf.h"
+#include "libbdk-hal/bdk-ecam.h"
+#include "libbdk-hal/device/bdk-device.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-driver/bdk-driver.h"
+#include "libbdk-hal/bdk-utils.h"
+
+static struct bdk_driver_s *driver_list = NULL;
+
+#define DEVICE_GROW 64
+static bdk_device_t *device_list = NULL;
+static int device_list_count = 0;
+static int device_list_max = 0;
+
+/**
+ * Called to register a new driver with the bdk-device system. Drivers are probed
+ * and initialized as device are found for them. If devices have already been
+ * added before the driver was registered, the driver will be probed and
+ * initialized before this function returns.
+ *
+ * @param driver Driver functions
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_add_driver(struct bdk_driver_s *driver)
+{
+    driver->next = driver_list;
+    driver_list = driver;
+    BDK_TRACE(DEVICE, "Added driver for %08x\n", driver->id);
+    return 0;
+}
+
+/**
+ * Lookup the correct driver for a device
+ *
+ * @param device Device to lookup
+ *
+ * @return Driver, or NULL on failure
+ */
+static const bdk_driver_t *lookup_driver(const bdk_device_t *device)
+{
+    const bdk_driver_t *drv = driver_list;
+    while (drv)
+    {
+        if (drv->id == device->id)
+            return drv;
+        drv = drv->next;
+    }
+    return NULL;
+}
+
+/**
+ * Populate the fields of a new device from the ECAM
+ *
+ * @param device Device to populate
+ */
+static void populate_device(bdk_device_t *device)
+{
+    /* The default name may be replaced by the driver with something easier to read */
+    snprintf(device->name, sizeof(device->name), "N%d.E%d:%d:%d.%d",
+        device->node, device->ecam, device->bus, device->dev, device->func);
+
+    BDK_TRACE(DEVICE_SCAN, "%s: Populating device\n", device->name);
+
+    /* Get the current chip ID and pass. We'll need this to fill in version
+       information for the device */
+    bdk_ap_midr_el1_t midr_el1;
+    BDK_MRS(MIDR_EL1, midr_el1.u);
+
+    /* PCCPF_XXX_VSEC_SCTL[RID] with the revision of the chip,
+       read from fuses */
+    BDK_CSR_DEFINE(sctl, BDK_PCCPF_XXX_VSEC_SCTL);
+    sctl.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_VSEC_SCTL);
+    sctl.s.rid = midr_el1.s.revision | (midr_el1.s.variant<<3);
+    sctl.s.node = device->node; /* Program node bits */
+    sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+        sctl.s.ea = 0; /* EA is not supported on CN88XX pass 1.x */
+    else
+        sctl.s.ea = bdk_config_get_int(BDK_CONFIG_PCIE_EA);
+    bdk_ecam_write32(device, BDK_PCCPF_XXX_VSEC_SCTL, sctl.u);
+
+    /* Read the Device ID */
+    device->id = bdk_ecam_read32(device, BDK_PCCPF_XXX_ID);
+
+    /* Read the Device Type so we know how to handle BARs */
+    bdk_pccpf_xxx_clsize_t clsize;
+    clsize.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CLSIZE);
+    int isbridge = (clsize.s.hdrtype & 0x7f) == 1;
+
+    BDK_TRACE(DEVICE_SCAN, "%s: Device ID: 0x%08x%s\n", device->name, device->id,
+        (isbridge) ? " (Bridge)" : "");
+
+    /* Loop through all the BARs */
+    int max_bar = (isbridge) ? BDK_PCCPF_XXX_BAR0U : BDK_PCCPF_XXX_BAR4U;
+    int bar = BDK_PCCPF_XXX_BAR0L;
+    unsigned guess_instance = 0;
+    while (bar <= max_bar)
+    {
+        int bar_index = (bar - BDK_PCCPF_XXX_BAR0L) / 8;
+        /* Read the BAR address and config bits [3:0] */
+        uint64_t address = bdk_ecam_read32(device, bar);
+        int ismem = !(address & 1);         /* Bit 0: 0 = mem, 1 = io */
+        int is64 = ismem && (address & 4);  /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
+        /* Bit 3: 1 = Is prefetchable. We on't care for now */
+
+        /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
+           it is using Enhanced Allocation (EA) */
+        if (!is64)
+        {
+            BDK_TRACE(DEVICE_SCAN, "%s: BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
+            bar += 8;
+            continue;
+        }
+
+        /* Get the upper part of 64bit BARs */
+        address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+
+        /* Write the bits to determine the size */
+        bdk_ecam_write32(device, bar, -1);
+        bdk_ecam_write32(device, bar + 4, -1);
+        uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+        size_mask |= bdk_ecam_read32(device, bar);
+        /* Make sure the node bits are correct in the address */
+        address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+        /* Restore address value */
+        bdk_ecam_write32(device, bar, address);
+        bdk_ecam_write32(device, bar + 4, address >> 32);
+
+        /* Convert the size into a power of 2 bits */
+        int size_bits = bdk_dpop(~size_mask | 0xf);
+        if (size_bits <= 4)
+            size_bits = 0;
+
+        /* Store the BAR info */
+        device->bar[bar_index].address = address & ~0xfull;
+        device->bar[bar_index].size2 = size_bits;
+        device->bar[bar_index].flags = address & 0xf;
+        BDK_TRACE(DEVICE_SCAN, "%s: BAR%d 0x%llx/%d flags=0x%x\n",
+            device->name, bar_index, device->bar[bar_index].address,
+            device->bar[bar_index].size2, device->bar[bar_index].flags);
+        /* Move to the next BAR */
+        bar += 8;
+    }
+
+    /* Walk the PCI capabilities looking for PCIe support and EA headers */
+    BDK_TRACE(DEVICE_SCAN, "%s: Walking PCI capabilites\n", device->name);
+    int has_pcie = 0;
+    bdk_pccpf_xxx_cap_ptr_t cap_ptr;
+    cap_ptr.u = bdk_ecam_read32(device, BDK_PCCPF_XXX_CAP_PTR);
+    int cap_loc = cap_ptr.s.cp;
+    while (cap_loc)
+    {
+        uint32_t cap = bdk_ecam_read32(device, cap_loc);
+        int cap_id = cap & 0xff;
+        int cap_next = (cap >> 8) & 0xff;
+
+        BDK_TRACE(DEVICE_SCAN, "%s:    PCI Capability 0x%02x ID:0x%02x Next:0x%02x\n",
+            device->name, cap_loc, cap_id, cap_next);
+
+        if (cap_id == 0x10)
+        {
+            BDK_TRACE(DEVICE_SCAN, "%s:      PCIe\n", device->name);
+            has_pcie = 1;
+        }
+        else if (cap_id == 0x01)
+        {
+            BDK_TRACE(DEVICE_SCAN, "%s:      PCI Power Management Interface\n", device->name);
+            /* Do nothing for now */
+        }
+        else if (cap_id == 0x11)
+        {
+            bdk_pccpf_xxx_msix_cap_hdr_t msix_cap_hdr;
+            bdk_pccpf_xxx_msix_table_t msix_table;
+            bdk_pccpf_xxx_msix_pba_t msix_pba;
+            msix_cap_hdr.u = cap;
+            msix_table.u = bdk_ecam_read32(device, cap_loc + 4);
+            msix_pba.u = bdk_ecam_read32(device, cap_loc + 8);
+            BDK_TRACE(DEVICE_SCAN, "%s:      MSI-X Entries:%d, Func Mask:%d, Enable:%d\n",
+                device->name, msix_cap_hdr.s.msixts + 1, msix_cap_hdr.s.funm, msix_cap_hdr.s.msixen);
+            BDK_TRACE(DEVICE_SCAN, "%s:          Table BAR%d, Offset:0x%x\n",
+                device->name, msix_table.s.msixtbir, msix_table.s.msixtoffs * 8);
+            BDK_TRACE(DEVICE_SCAN, "%s:          PBA BAR%d, Offset:0x%x\n",
+                device->name, msix_pba.s.msixpbir, msix_pba.s.msixpoffs * 8);
+        }
+        else if (cap_id == 0x05)
+        {
+            BDK_TRACE(DEVICE_SCAN, "%s:      MSI\n", device->name);
+            /* Do nothing for now */
+        }
+        else if (cap_id == 0x14)
+        {
+            bdk_pccpf_xxx_ea_cap_hdr_t ea_cap_hdr;
+            ea_cap_hdr.u = cap;
+            cap_loc += 4;
+            BDK_TRACE(DEVICE_SCAN, "%s:      Enhanced Allocation, %d entries\n",
+                device->name, ea_cap_hdr.s.num_entries);
+            if (isbridge)
+            {
+                cap = bdk_ecam_read32(device, cap_loc);
+                cap_loc += 4;
+                int fixed_secondary_bus = cap & 0xff;
+                int fixed_subordinate_bus = cap & 0xff;
+                BDK_TRACE(DEVICE_SCAN, "%s:      Fixed Secondary Bus:0x%02x Fixed Subordinate Bus:0x%02x\n",
+                    device->name, fixed_secondary_bus, fixed_subordinate_bus);
+            }
+            for (int entry = 0; entry < ea_cap_hdr.s.num_entries; entry++)
+            {
+                union bdk_pcc_ea_entry_s ea_entry;
+                memset(&ea_entry, 0, sizeof(ea_entry));
+                uint32_t *ptr = (uint32_t *)&ea_entry;
+                *ptr++ = bdk_ecam_read32(device, cap_loc);
+#if __BYTE_ORDER == __BIG_ENDIAN
+                /* For big endian we actually need the previous data
+                   shifted 32 bits */
+                *ptr = ptr[-1];
+#endif
+                asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
+                int entry_size = ea_entry.s.entry_size;
+                for (int i = 0; i < entry_size; i++)
+                {
+                    *ptr++ = bdk_ecam_read32(device, cap_loc + 4*i + 4);
+                }
+#if __BYTE_ORDER == __BIG_ENDIAN
+                /* The upper and lower 32bits need to be swapped */
+                ea_entry.u[0] = (ea_entry.u[0] >> 32) | (ea_entry.u[0] << 32);
+                ea_entry.u[1] = (ea_entry.u[1] >> 32) | (ea_entry.u[1] << 32);
+                ea_entry.u[2] = (ea_entry.u[2] >> 32) | (ea_entry.u[2] << 32);
+#endif
+                asm volatile ("" ::: "memory"); /* Needed by gcc 5.0 to detect aliases on ea_entry */
+                BDK_TRACE(DEVICE_SCAN, "%s:      Enable:%d Writeable:%d Secondary Prop:0x%02x Primary Prop:0x%02x BEI:%d Size:%d\n",
+                    device->name, ea_entry.s.enable, ea_entry.s.w, ea_entry.s.sec_prop, ea_entry.s.pri_prop, ea_entry.s.bei, ea_entry.s.entry_size);
+                if (ea_entry.s.entry_size > 0)
+                {
+                    BDK_TRACE(DEVICE_SCAN, "%s:        Base:0x%08x 64bit:%d\n",
+                        device->name, ea_entry.s.basel << 2, ea_entry.s.base64);
+                }
+                if (ea_entry.s.entry_size > 1)
+                {
+                    BDK_TRACE(DEVICE_SCAN, "%s:        MaxOffset:0x%08x 64bit:%d\n",
+                        device->name, (ea_entry.s.offsetl << 2) | 3, ea_entry.s.offset64);
+                }
+                if (ea_entry.s.entry_size > 2)
+                {
+                    BDK_TRACE(DEVICE_SCAN, "%s:        BaseUpper:0x%08x\n",
+                        device->name, ea_entry.s.baseh);
+                }
+                if (ea_entry.s.entry_size > 3)
+                {
+                    BDK_TRACE(DEVICE_SCAN, "%s:        MaxOffsetUpper:0x%08x\n",
+                        device->name, ea_entry.s.offseth);
+                }
+                if (ea_entry.s.enable)
+                {
+                    uint64_t base = (uint64_t)ea_entry.s.baseh << 32;
+                    base |= (uint64_t)ea_entry.s.basel << 2;
+                    /* Make sure the node bits are correct in the address */
+                    base = (base & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+                    uint64_t offset = (uint64_t)ea_entry.s.offseth << 32;
+                    offset |= ((uint64_t)ea_entry.s.offsetl << 2) | 3;
+                    switch (ea_entry.s.bei)
+                    {
+                        case 0: /* BAR 0 */
+                        case 2: /* BAR 1 */
+                        case 4: /* BAR 2 */
+                        {
+                            int bar_index = ea_entry.s.bei/2;
+                            device->bar[bar_index].address = base;
+                            device->bar[bar_index].size2 = bdk_dpop(offset);
+                            device->bar[bar_index].flags = ea_entry.s.base64 << 2;
+                            BDK_TRACE(DEVICE_SCAN, "%s:        Updated BAR%d 0x%llx/%d flags=0x%x\n",
+                                device->name, bar_index, device->bar[bar_index].address,
+                                device->bar[bar_index].size2, device->bar[bar_index].flags);
+                            if (0 == ea_entry.s.bei) {
+                                /* PEMs eg PCIEEP and PCIERC do not have instance id
+                                ** We can calculate it for PCIERC based on BAR0 allocation.
+                                ** PCIEEP will be dropped by probe
+                                */
+                                guess_instance = (device->bar[bar_index].address >> 24) & 7;
+                            }
+                            break;
+                        }
+                        case 9: /* SR-IOV BAR 0 */
+                        case 11: /* SR-IOV BAR 1 */
+                        case 13: /* SR-IOV BAR 2 */
+                            // FIXME
+                            break;
+                    }
+                }
+                cap_loc += ea_entry.s.entry_size * 4 + 4;
+            }
+        }
+        else
+        {
+            /* Unknown PCI capability */
+            bdk_warn("%s: ECAM device unknown PCI capability 0x%x\n", device->name, cap_id);
+        }
+        cap_loc = cap_next;
+    }
+
+    /* Walk the PCIe capabilities looking for instance header */
+    if (has_pcie)
+    {
+        BDK_TRACE(DEVICE_SCAN, "%s: Walking PCIe capabilites\n", device->name);
+        cap_loc = 0x100;
+        while (cap_loc)
+        {
+            uint32_t cap = bdk_ecam_read32(device, cap_loc);
+            int cap_id = cap & 0xffff;
+            int cap_ver = (cap >> 16) & 0xf;
+            int cap_next = cap >> 20;
+            BDK_TRACE(DEVICE_SCAN, "%s:    PCIe Capability 0x%03x ID:0x%04x Version:0x%x Next:0x%03x\n",
+                device->name, cap_loc, cap_id, cap_ver, cap_next);
+            if (cap_id == 0xe)
+            {
+                /* ARI. Do nothing for now */
+                BDK_TRACE(DEVICE_SCAN, "%s:      ARI\n", device->name);
+            }
+            else if (cap_id == 0xb)
+            {
+                /* Vendor specific*/
+                int vsec_id = bdk_ecam_read32(device, cap_loc + 4);
+                int vsec_id_id = vsec_id & 0xffff;
+                int vsec_id_rev = (vsec_id >> 16) & 0xf;
+                int vsec_id_len = vsec_id >> 20;
+                BDK_TRACE(DEVICE_SCAN, "%s:      Vendor ID: 0x%04x Rev: 0x%x Size 0x%03x\n",
+                    device->name, vsec_id_id, vsec_id_rev, vsec_id_len);
+                switch (vsec_id_id)
+                {
+                    case 0x0001: /* RAS Data Path */
+                        BDK_TRACE(DEVICE_SCAN, "%s:      Vendor RAS Data Path\n", device->name);
+                        break;
+
+                    case 0x0002: /* RAS DES */
+                        BDK_TRACE(DEVICE_SCAN, "%s:      Vendor RAS DES\n", device->name);
+                        break;
+
+                    case 0x00a0: /* Cavium common */
+                    case 0x00a1: /* Cavium CN88XX */
+                    case 0x00a2: /* Cavium CN81XX */
+                    case 0x00a3: /* Cavium CN83XX */
+                        if ((vsec_id_rev == 1) || (vsec_id_rev == 2))
+                        {
+                            int vsec_ctl = bdk_ecam_read32(device, cap_loc + 8);
+                            int vsec_ctl_inst_num = vsec_ctl & 0xff;
+                            int vsec_ctl_subnum = (vsec_ctl >> 8) & 0xff;
+                            BDK_TRACE(DEVICE_SCAN, "%s:        Cavium Instance: 0x%02x Static Bus: 0x%02x\n",
+                                device->name, vsec_ctl_inst_num, vsec_ctl_subnum);
+                            int vsec_sctl = bdk_ecam_read32(device, cap_loc + 12);
+                            int vsec_sctl_rid = (vsec_sctl >> 16) & 0xff;
+                            if (vsec_id_rev == 2)
+                            {
+                                int vsec_sctl_pi = (vsec_sctl >> 24) & 0xff; /* Only in Rev 2 */
+                                BDK_TRACE(DEVICE_SCAN, "%s:        Revision ID: 0x%02x Programming Interface: 0x%02x\n",
+                                    device->name, vsec_sctl_rid, vsec_sctl_pi);
+                            }
+                            else
+                            {
+                                BDK_TRACE(DEVICE_SCAN, "%s:        Revision ID: 0x%02x\n",
+                                    device->name, vsec_sctl_rid);
+                            }
+                            /* Record the device instance */
+                            device->instance = vsec_ctl_inst_num;
+                        }
+                        else
+                        {
+                            bdk_warn("%s: ECAM device Unknown Cavium extension revision\n", device->name);
+                        }
+                        break;
+
+                    default: /* Unknown Vendor extension */
+                        bdk_warn("%s: ECAM device unknown vendor extension ID 0x%x\n", device->name, vsec_id_id);
+                        break;
+                }
+            }
+            else if (cap_id == 0x10)
+            {
+                /* Single Root I/O Virtualization (SR-IOV) */
+                BDK_TRACE(DEVICE_SCAN, "%s:      SR-IOV\n", device->name);
+                /* Loop through all the SR-IOV BARs */
+                bar = cap_loc + 0x24;
+                while (bar <= (cap_loc + 0x3c))
+                {
+                    int bar_index = (bar - 0x24 - cap_loc) / 8;
+                    /* Read the BAR address and config bits [3:0] */
+                    uint64_t address = bdk_ecam_read32(device, bar);
+                    int ismem = !(address & 1);         /* Bit 0: 0 = mem, 1 = io */
+                    int is64 = ismem && (address & 4);  /* Bit 2: 0 = 32 bit, 1 = 64 bit if mem */
+                    /* Bit 3: 1 = Is prefetchable. We don't care for now */
+
+                    /* All internal BARs should be 64 bit. Skip if BAR isn't as that means
+                       it is using Enhanced Allocation (EA) */
+                    if (!is64)
+                    {
+                        BDK_TRACE(DEVICE_SCAN, "%s:        SR-IOV BAR%d Disabled or EA bar skipped (0x%08llx)\n", device->name, bar_index, address);
+                        bar += 8;
+                        continue;
+                    }
+
+                    /* Get the upper part of 64bit BARs */
+                    address |= (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+
+                    /* Write the bits to determine the size */
+                    bdk_ecam_write32(device, bar, -1);
+                    bdk_ecam_write32(device, bar + 4, -1);
+                    uint64_t size_mask = (uint64_t)bdk_ecam_read32(device, bar + 4) << 32;
+                    size_mask |= bdk_ecam_read32(device, bar);
+                    /* Make sure the node bits are correct in the address */
+                    address = (address & ~(3UL << 44)) | ((uint64_t)device->node << 44);
+                    /* Restore address value */
+                    bdk_ecam_write32(device, bar, address);
+                    bdk_ecam_write32(device, bar + 4, address >> 32);
+
+                    /* Convert the size into a power of 2 bits */
+                    int size_bits = bdk_dpop(size_mask | 0xf);
+                    if (size_bits <= 4)
+                        size_bits = 0;
+
+                    BDK_TRACE(DEVICE_SCAN, "%s:        SR-IOV BAR%d 0x%llx/%d flags=0x%llx\n",
+                        device->name, bar_index, address & ~0xfull,
+                        size_bits, address & 0xf);
+                    /* Move to the next BAR */
+                    bar += 8;
+                }
+            }
+            else if (cap_id == 0x01)
+            {
+                /* Advanced Error Reporting Capability */
+                BDK_TRACE(DEVICE_SCAN, "%s:      Advanced Error Reporting\n", device->name);
+            }
+            else if (cap_id == 0x19)
+            {
+                /* Secondary PCI Express Extended Capability */
+                BDK_TRACE(DEVICE_SCAN, "%s:      Secondary PCI Express Extended\n", device->name);
+            }
+            else if (cap_id == 0x15)
+            {
+                /* PCI Express Resizable BAR (RBAR) Capability */
+                BDK_TRACE(DEVICE_SCAN, "%s:      PCI Express Resizable BAR (RBAR)\n", device->name);
+            }
+            else if (cap_id == 0x0d)
+            {
+                /* Extended access control := ACS Extended Capability */
+                BDK_TRACE(DEVICE_SCAN, "%s:      ACS\n", device->name);
+            }
+            else
+            {
+                /* Unknown PCIe capability */
+                bdk_warn("%s: ECAM device unknown PCIe capability 0x%x\n", device->name, cap_id);
+            }
+            cap_loc = cap_next;
+        }
+    }
+    else
+    {
+        bdk_error("%s: ECAM device didn't have a PCIe capability\n", device->name);
+    }
+    if (BDK_NO_DEVICE_INSTANCE == device->instance) {
+        device->instance = guess_instance;
+    }
+    BDK_TRACE(DEVICE_SCAN, "%s: Device populated\n", device->name);
+}
+
+/**
+ * Called by the ECAM code whan a new device is detected in the system
+ *
+ * @param node   Node the ECAM is on
+ * @param ecam   ECAM the device is on
+ * @param bus    Bus number for the device
+ * @param dev    Device number
+ * @param func   Function number
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_add(bdk_node_t node, int ecam, int bus, int dev, int func)
+{
+    if (device_list_count == device_list_max)
+    {
+        int grow = device_list_max + DEVICE_GROW;
+        bdk_device_t *tmp = malloc(grow * sizeof(bdk_device_t));
+        if (!tmp)
+            memcpy(tmp, device_list, device_list_max * sizeof(bdk_device_t));
+        free(device_list);
+        if (tmp == NULL)
+        {
+            bdk_error("bdk-device: Failed to allocate space for device\n");
+            return -1;
+        }
+        device_list = tmp;
+        device_list_max = grow;
+    }
+
+    bdk_device_t *device = &device_list[device_list_count++];
+    memset(device, 0, sizeof(*device));
+
+    device->state = BDK_DEVICE_STATE_NOT_PROBED;
+    device->node = node;
+    device->ecam = ecam;
+    device->bus = bus;
+    device->dev = dev;
+    device->func = func;
+    device->instance = BDK_NO_DEVICE_INSTANCE;
+    populate_device(device);
+
+    const bdk_driver_t *drv = lookup_driver(device);
+    if (drv)
+        BDK_TRACE(DEVICE, "%s: Added device\n", device->name);
+    else
+        BDK_TRACE(DEVICE, "%s: Added device without driver (0x%08x)\n", device->name, device->id);
+    return 0;
+}
+
+/**
+ * Rename a device. Called by driver to give devices friendly names
+ *
+ * @param device Device to rename
+ * @param format Printf style format string
+ */
+void bdk_device_rename(bdk_device_t *device, const char *format, ...)
+{
+    char tmp[sizeof(device->name)];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(tmp, sizeof(tmp), format, args);
+    va_end(args);
+    tmp[sizeof(tmp) - 1] = 0;
+    BDK_TRACE(DEVICE, "%s: Renamed to %s\n", device->name, tmp);
+    strcpy(device->name, tmp);
+}
+
+/**
+ * Called by the ECAM code once all devices have been added
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_device_init(void)
+{
+    /* Probe all devices first */
+    for (int i = 0; i < device_list_count; i++)
+    {
+        bdk_device_t *dev = &device_list[i];
+        const bdk_driver_t *drv = lookup_driver(dev);
+        if (drv == NULL)
+            continue;
+        if (dev->state == BDK_DEVICE_STATE_NOT_PROBED)
+        {
+            BDK_TRACE(DEVICE, "%s: Probing\n", dev->name);
+            if (drv->probe(dev))
+            {
+                BDK_TRACE(DEVICE, "%s: Probe failed\n", dev->name);
+                dev->state = BDK_DEVICE_STATE_PROBE_FAIL;
+            }
+            else
+            {
+                BDK_TRACE(DEVICE, "%s: Probe complete\n", dev->name);
+                dev->state = BDK_DEVICE_STATE_PROBED;
+            }
+        }
+    }
+
+    /* Do init() after all the probes. See comments in top of bdk-device.h */
+    for (int i = 0; i < device_list_count; i++)
+    {
+        bdk_device_t *dev = &device_list[i];
+        const bdk_driver_t *drv = lookup_driver(dev);
+        if (drv == NULL)
+            continue;
+        if (dev->state == BDK_DEVICE_STATE_PROBED)
+        {
+            BDK_TRACE(DEVICE, "%s: Initializing\n", dev->name);
+            if (drv->init(dev))
+            {
+                BDK_TRACE(DEVICE, "%s: Init failed\n", dev->name);
+                dev->state = BDK_DEVICE_STATE_INIT_FAIL;
+            }
+            else
+            {
+                BDK_TRACE(DEVICE, "%s: Init complete\n", dev->name);
+                dev->state = BDK_DEVICE_STATE_READY;
+            }
+        }
+    }
+    return 0;
+}
+
+/**
+ * Lookup a device by ECAM ID and internal instance number. This can be used by
+ * one device to find a handle to an associated device. For example, PKI would
+ * use this function to get a handle to the FPA.
+ *
+ * @param node     Node to lookup for
+ * @param id       ECAM ID
+ * @param instance Cavium internal instance number
+ *
+ * @return Device pointer, or NULL if the device isn't found
+ */
+const bdk_device_t *bdk_device_lookup(bdk_node_t node, uint32_t id, int instance)
+{
+    for (int i = 0; i < device_list_count; i++)
+    {
+        bdk_device_t *dev = &device_list[i];
+        if ((dev->node == node) && (dev->id == id) && (dev->instance == instance))
+            return dev;
+    }
+    BDK_TRACE(DEVICE, "No device found for node %d, ID %08x, instance %d\n", node, id, instance);
+    return NULL;
+}
+
+/**
+ * Read from a device BAR
+ *
+ * @param device Device to read from
+ * @param bar    Which BAR to read from (0-3)
+ * @param size   Size of the read
+ * @param offset Offset into the BAR
+ *
+ * @return Value read
+ */
+uint64_t bdk_bar_read(const bdk_device_t *device, int bar, int size, uint64_t offset)
+{
+    uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
+    address += device->bar[bar/2].address;
+    if (offset+size > (1ULL << device->bar[bar/2].size2)) {
+        /* The CSR address passed in offset doesn't contain the node number. Copy it
+           from the BAR address */
+        offset |= address & (0x3ull << 44);
+        if (address != offset)
+            bdk_fatal("BAR read address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
+    }
+    switch (size)
+    {
+        case 1:
+            return bdk_read64_uint8(address);
+        case 2:
+            return bdk_le16_to_cpu(bdk_read64_uint16(address));
+        case 4:
+            return bdk_le32_to_cpu(bdk_read64_uint32(address));
+        case 8:
+            return bdk_le64_to_cpu(bdk_read64_uint64(address));
+    }
+    bdk_fatal("%s: Unexpected read size %d\n", device->name, size);
+}
+
+/**
+ * Write to a device BAR
+ *
+ * @param device Device to write to
+ * @param bar    Which BAR to read from (0-3)
+ * @param size   Size of the write
+ * @param offset Offset into the BAR
+ * @param value  Value to write
+ */
+void bdk_bar_write(const bdk_device_t *device, int bar, int size, uint64_t offset, uint64_t value)
+{
+    uint64_t address = offset & bdk_build_mask(device->bar[bar/2].size2);
+    address += device->bar[bar/2].address;
+    if (offset+size > (1ULL << device->bar[bar/2].size2)) {
+        /* The CSR address passed in offset doesn't contain the node number. Copy it
+           from the BAR address */
+        offset |= address & (0x3ull << 44);
+        if (address != offset)
+            bdk_fatal("BAR write address 0x%llx doesn't match CSR address 0x%llx\n", address, offset);
+    }
+    switch (size)
+    {
+        case 1:
+            bdk_write64_uint8(address, value);
+            return;
+        case 2:
+            bdk_write64_uint16(address, bdk_cpu_to_le16(value));
+            return;
+        case 4:
+            bdk_write64_uint32(address, bdk_cpu_to_le32(value));
+            return;
+        case 8:
+            bdk_write64_uint64(address, bdk_cpu_to_le64(value));
+            return;
+    }
+    bdk_fatal("%s: Unexpected write size %d\n", device->name, size);
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-marvell.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-marvell.c
new file mode 100644
index 0000000..e8f3a00
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-marvell.c
@@ -0,0 +1,115 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2016  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-hal/bdk-mdio.h>
+#include <libbdk-hal/bdk-qlm.h>
+#include <libbdk-hal/if/bdk-if.h>
+
+/**
+ * Setup marvell PHYs
+ * This function sets up one port in a marvell 88E1512 in SGMII mode
+ */
+static void setup_marvell_phy(bdk_node_t node, int mdio_bus, int mdio_addr)
+{
+      int phy_status = 0;
+
+           BDK_TRACE(PHY, "%s In SGMII mode for Marvell PHY 88E1512\n", __FUNCTION__);
+          /* Switch to Page 18 */
+            phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 22, 18);
+          if (phy_status < 0)
+                       return;
+
+          phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 22);
+          if (phy_status < 0)
+                       return;
+
+          /* Change the Phy System mode from RGMII(default hw reset mode) to SGMII */
+          phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 20, 1);
+          if (phy_status < 0)
+                return;
+
+          /* Requires a Software reset */
+          phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 20, 0x8001);
+          if (phy_status < 0)
+                return;
+
+          phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 20);
+          if (phy_status < 0)
+                       return;
+
+          /* Change the Page back to 0 */
+            phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 22, 0);
+          if (phy_status < 0)
+                       return;
+
+          phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 22);
+          if (phy_status < 0)
+                       return;
+      
+      phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17);
+      if (phy_status < 0)
+           return;
+}
+
+int bdk_if_phy_marvell_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr)
+{
+    BDK_TRACE(PHY,"In %s\n",__FUNCTION__);
+
+    /* Check if the PHY is marvell PHY we expect */
+    int phy_status = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
+    if (phy_status != 0x0141)
+        return 0;
+
+    /* Check that the GSER mode is SGMII */
+    /* Switch the marvell PHY to the correct mode */
+    bdk_qlm_modes_t qlm_mode = bdk_qlm_get_mode(node, qlm);
+    
+    BDK_TRACE(PHY,"%s: QLM:%d QLM_MODE:%d\n",__FUNCTION__, qlm, qlm_mode);
+    
+    if ((qlm_mode != BDK_QLM_MODE_SGMII_1X1) &&
+        (qlm_mode != BDK_QLM_MODE_SGMII_2X1))
+        return 0;
+
+    BDK_TRACE(PHY,"%s: Detected Marvell Phy in SGMII mode\n", __FUNCTION__);    
+    for (int port = 0; port < 2; port++)
+    {
+        setup_marvell_phy(node, mdio_bus, phy_addr + port);
+    }
+    return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-8514.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-8514.c
new file mode 100644
index 0000000..4d6ef8d
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-8514.c
@@ -0,0 +1,224 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2016  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-hal/if/bdk-if.h>
+#include <libbdk-hal/bdk-mdio.h>
+#include <libbdk-hal/bdk-qlm.h>
+
+#define VSC_PHY_STD_PAGE     (0x0)
+#define VSC_PHY_EXT1_PAGE    (0x1)
+#define VSC_PHY_EXT2_PAGE    (0x2)
+#define VSC_PHY_EXT3_PAGE    (0x3)
+#define VSC_PHY_EXT4_PAGE    (0x4)
+#define VSC_PHY_GPIO_PAGE    (0x10)
+#define VSC_PHY_TEST_PAGE    (0x2A30)
+#define VSC_PHY_TR_PAGE      (0x52B5)
+
+const uint16_t init_script_rev_a[] = {
+//     Op,   Page,  Reg, Value,  Mask
+//      0,      1,    2,     3,     4
+//     --, ------, ----, ------, -----
+        0, 0x0000, 0x1f, 0x0000, 0xffff,
+        1, 0x0000, 0x16, 0x0001, 0x0001,
+        0, 0x0001, 0x1f, 0x2A30, 0xffff,
+        1, 0x2A30, 0x08, 0x8000, 0x8000,
+        0, 0x2A30, 0x1f, 0x52B5, 0xffff,
+        0, 0x52B5, 0x12, 0x0068, 0xffff,
+        0, 0x52B5, 0x11, 0x8980, 0xffff,
+        0, 0x52B5, 0x10, 0x8f90, 0xffff,
+        0, 0x52B5, 0x12, 0x0000, 0xffff,
+        0, 0x52B5, 0x11, 0x0003, 0xffff,
+        0, 0x52B5, 0x10, 0x8796, 0xffff,
+        0, 0x52B5, 0x12, 0x0050, 0xffff,
+        0, 0x52B5, 0x11, 0x100f, 0xffff,
+        0, 0x52B5, 0x10, 0x87fa, 0xffff,
+        0, 0x52B5, 0x1f, 0x2A30, 0xffff,
+        1, 0x2A30, 0x08, 0x0000, 0x8000,
+        0, 0x2A30, 0x1f, 0x0000, 0xffff,
+        1, 0x0000, 0x16, 0x0000, 0x0001,
+      0xf, 0xffff, 0xff, 0xffff, 0xffff
+};
+
+static void wr_masked(bdk_node_t node, int mdio_bus, int phy_addr, int reg, int value, int mask)
+{
+    int nmask = ~mask;
+    int old = bdk_mdio_read(node, mdio_bus, phy_addr, reg);
+    int vmask = value & mask;
+    int newv = old & nmask;
+    newv = newv | vmask;
+    bdk_mdio_write(node, mdio_bus, phy_addr, reg, newv);
+}
+static void vitesse_init_script(bdk_node_t node, int mdio_bus, int phy_addr)
+{
+    const uint16_t    *ptr;
+    uint16_t    reg_addr;
+    uint16_t    reg_val;
+    uint16_t    mask;
+    
+    BDK_TRACE(PHY,"In %s\n",__FUNCTION__);
+    BDK_TRACE(PHY,"Loading init script for VSC8514\n");
+
+    ptr = init_script_rev_a;
+    while (*ptr != 0xf)
+    {
+        reg_addr = *(ptr+2);
+        reg_val = *(ptr+3);
+        mask = *(ptr+4);
+        ptr+=5;
+        if (mask != 0xffff)
+        {
+            wr_masked(node, mdio_bus, phy_addr, reg_addr,reg_val,mask);
+        }
+        else
+        {
+            bdk_mdio_write(node,mdio_bus,phy_addr,reg_addr,reg_val);
+        }
+    }
+
+    BDK_TRACE(PHY,"loading init script is done\n");
+
+}
+
+static void vitesse_program(bdk_node_t node, int mdio_bus, int phy_addr)
+{
+    return;
+}
+
+/**
+ * Setup Vitesse PHYs
+ * This function sets up one port in a Vitesse VSC8514 
+ */
+static void setup_vitesse_phy(bdk_node_t node, int mdio_bus, int phy_addr)
+{
+    /*setting MAC if*/
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, VSC_PHY_GPIO_PAGE);
+    wr_masked(node,mdio_bus,phy_addr, 19, 0x4000, 0xc000); 
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, 0x80e0);
+
+    /*Setting media if*/
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, VSC_PHY_STD_PAGE);
+    // Reg23, 10:8 Select copper,  CAT5 copper only
+    wr_masked(node,mdio_bus,phy_addr, 23, 0x0000, 0x0700);
+    
+    // Reg0:15, soft Reset
+    wr_masked(node,mdio_bus,phy_addr, 0, 0x8000, 0x8000);
+    int time_out = 100;
+    while (time_out && bdk_mdio_read(node,mdio_bus,phy_addr, 0) & 0x8000)
+    {
+        bdk_wait_usec(100000);
+        time_out--;
+    }
+
+    if (time_out == 0)
+    {
+        BDK_TRACE(PHY,"setting PHY TIME OUT\n");
+        return;
+    }
+    else
+    {
+        BDK_TRACE(PHY,"Setting a phy port is done\n");
+    }
+
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, VSC_PHY_EXT3_PAGE);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 16, 0x80);
+    // Select main registers
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, VSC_PHY_STD_PAGE);
+    
+  /*
+  
+    if (LOOP_INTERNAL)
+    {
+        reg0 = bdk_mdio_read(node, mdio_bus, phy_addr, 0);
+        reg0 = bdk_insert(reg0, 1, 14, 1);
+        bdk_mdio_write(node, mdio_bus, phy_addr, 0, reg0);
+    }
+
+    // Far end loopback (External side)
+    if (LOOP_EXTERNAL)
+    {
+        reg23 = bdk_mdio_read(node, mdio_bus, phy_addr, 23);
+        reg23 = bdk_insert(reg23, 1, 3, 1);
+        bdk_mdio_write(node, mdio_bus, phy_addr, 23, reg23);
+    }
+    
+
+    // Dump registers
+    if (false)
+    {
+        printf("\nVitesse PHY register dump, PHY address %d, mode %s\n",
+               phy_addr, (qsgmii) ? "QSGMII" : "SGMII");
+        int phy_addr = 4;
+        for (int reg_set = 0; reg_set <= 0x10; reg_set += 0x10)
+        {
+            printf("\nDump registers with reg[31]=0x%x\n", reg_set);
+            bdk_mdio_write(node, mdio_bus, phy_addr, 31, reg_set);
+            for (int reg=0; reg < 32; reg++)
+                printf("reg[%02d]=0x%x\n", reg, bdk_mdio_read(node, mdio_bus, phy_addr, reg));
+        }
+    }
+    */
+}
+
+//static void vetesse_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr)
+int bdk_if_phy_vsc8514_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr)
+{
+    /* Check if the PHY is Vetesse PHY we expect */
+    int phy_status_1 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
+    int phy_status_2 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID2);
+    if (phy_status_1 != 0x0007 || phy_status_2 != 0x0670)
+    {
+         bdk_error("The PHY on this board is NOT VSC8514.\n");
+         return -1;
+    }
+
+    /* Check that the GSER mode is SGMII or QSGMII */
+    bdk_qlm_modes_t qlm_mode = bdk_qlm_get_mode(node, qlm);
+    if (qlm_mode != BDK_QLM_MODE_QSGMII_4X1)
+        return -1;
+
+    vitesse_init_script(node, mdio_bus, phy_addr);
+    vitesse_program(node, mdio_bus, phy_addr);
+
+    /* VSC8514 just support QSGMII */
+    for (int port = 0; port < 4; port++)
+        setup_vitesse_phy(node, mdio_bus, phy_addr + port);
+
+    return 1;
+
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-xfi.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-xfi.c
new file mode 100644
index 0000000..b6f052a
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse-xfi.c
@@ -0,0 +1,395 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-hal/if/bdk-if.h>
+#include <libbdk-hal/bdk-mdio.h>
+#include <libbdk-hal/bdk-twsi.h>
+
+/* This code is an optional part of the BDK. It is only linked in
+ *     if BDK_REQUIRE() needs it */
+//BDK_REQUIRE(TWSI);
+BDK_REQUIRE_DEFINE(XFI);
+
+/*
+Rate Select Settings
+Mode State			: 6/8 
+Rate Select State	: 0 
+RSEL1				: 0 
+RSEL0				: 0 
+Ref Clock Gen(MHz)	: 156.25
+Data Rate(Gbps)		: 10.3125
+Description			: 10 GbE
+
+
+Data Rate Detection Configuration Registers
+
+Mode Pin Settings:
+Mode State	: 0 
+MODE1		: 0 
+MODE0		: 0 
+Mode		: Two-wire serial interface mode
+
+LOS Pin Strap Mode Settings
+Mode State			: 2/6/8 
+State				: 4 
+LOS1				: Float
+LOS0				: Float 
+LOS Amplitude(mVpp)	: 20 
+LOS Hysteresis(dB)	: 2
+
+Input Equalization Retimer Mode Settings
+Mode State		: 6/8
+EQ State		: 0 
+EQ1				: 0 
+EQ0				: 0 
+EQ(dB)			: Auto 
+DFE				: Auto 
+Comment			: Full Auto
+
+Input Equalization Re-Driver Mode Settings
+Mode State		: 
+EQ State		: 0 
+EQ1				: 0 
+EQ0				: 0 
+EQ(dB)			: Auto 
+DFE				: APowered Down 
+Comment			: Analog EQ Only
+
+
+
+Output De-Emphasis Retimer Mode Settings
+Mode State		: 6/8 
+DE State		: 3 
+TX1				: Float 
+TX0				: 0 
+PRE  c(-1) mA	: -1 
+MAIN c( 0) mA	: 15
+POST c(+1) mA	: 4 
+DC Amplitude(mV): 500
+De-Emphasis(dB)	: -6.02
+Comment			: 
+
+
+Output De-Emphasis Re-Driver Mode Settings
+Mode State		: 2 
+DE State		: 3 
+TX1				: Float 
+TX0				: 0 
+Frequency(Gbps)	: 10.3125 
+DC Amplitude(mV): 600
+De-Emphasis(dB)	: 4 
+Comment			: 10GbE
+
+
+*/
+
+static int debug = 0;
+
+#define xfi_printf(fmt, args...)		\
+	do {					\
+		if(debug == 1){			\
+			printf(fmt, ##args);	\
+		}				\
+	} while(0)
+
+
+int bdk_xfi_vsc7224_dump(int twsi_id, int unit){
+	bdk_node_t node=0;
+	uint8_t dev_addr=0x10 + unit;
+	uint16_t internal_addr=0x7F;
+	int num_bytes=2;
+	int ia_width_bytes=1;
+	uint64_t data=0;
+	int p, i;
+	uint64_t result[0x100] = {0};
+
+	uint64_t pagenum[9] = {0x00, 0x01, 0x02, 0x03, 0x20, 0x21, 0x30, 0x31, 0x40};
+
+	for(p=0; p < (sizeof(pagenum)/sizeof(pagenum[0])); p++){
+		data = pagenum[p];
+		bdk_twsix_write_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, data);
+		for(i=0x80; i<=0xFF; i++){
+			result[i] = 0x00;
+			result[i] = bdk_twsix_read_ia(node, twsi_id, dev_addr, (uint16_t)i, num_bytes, ia_width_bytes);
+		}
+		for(i=0x80; i<=0xFF; i++){
+			if(i==0x80){
+				printf("\npage_%02X[0x100] = {\n", (uint8_t)pagenum[p]);
+			}
+			if(i % 8 == 0){
+				printf("/* 0x%2X */", i);
+			}
+			printf(" 0x%04X,", (uint16_t)result[i]);
+			if(i==0xFF){
+				printf("};");
+			}
+			if((i+1) % 8 == 0){
+				printf("\n");
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* XFI ReTimer/ReDriver Mode Settings */
+
+/*
+power down regs:
+Page Reg Position Mask val RegFieldName
+0x00 0x89 b07 0x0080 1 PD_INBUF
+0x00 0x8A b10 0x0400 1 PD_DFECRU
+0x00 0x8A b01 0x0002 1 PD_DFE
+0x00 0x8A b00 0x0001 1 PD_DFEADAPT
+0x00 0x97 b15 0x8000 1 ASYN_SYNN
+0x00 0x97 b09 0x0200 1 PD_OD
+0x00 0xA0 b11 0x0800 1 PD_LOS
+0x00 0xA4 b15 0x8000 1 PD_CH
+0x00 0xB5 b07 0x0080 1 PD_INBUF 
+0x00 0xB9 b15 0x8000 1 ASYN_SYNN 
+0x00 0xB9 b09 0x0200 1 PD_OD
+0x00 0xBF b07 0x0080 1 PD_INBUF 
+0x00 0xF0 b15 0x8000 1 ASYN_SYNN
+0x00 0xF0 b09 0x0200 1 PD_OD
+0x00 0xF6 b07 0x0080 1 PD_INBUF
+0x00 0xFA b15 0x8000 1 ASYN_SYNN
+0x00 0xFA b09 0x0200 1 PD_OD
+*/
+struct regmap{
+	short int page;
+	unsigned char reg;
+	unsigned short int retimer;
+	unsigned short int redriver;
+};
+
+/* This table only applies to SFF8104 */
+struct regmap xfiregmap[64] = {
+//CH 0
+{0x00, 0x84, 0x0800, 0x0000}, //EQTABLE_DCOFF0 (0n_84)
+{0x00, 0x8A, 0x7000, 0x0400}, //DFECRU_CTRL (0n_8A)
+{0x00, 0x8B, 0x4060, 0x0000}, //DFECRU_CFVF_CFAP (0n_8B)
+{0x00, 0x90, 0xDE85, 0x0000}, //DFECRU_DFEAUTO (0n_90)
+{0x00, 0x91, 0x2020, 0x0000}, //DFECRU_BTMX_BFMX (0n_91)
+{0x00, 0x92, 0x0860, 0x0000}, //DFECRU_DXMX_TRMX (0n_92)
+{0x00, 0x93, 0x6000, 0x0000}, //DFECRU_TRMN_ERRI (0n_93)
+{0x00, 0x94, 0x0001, 0x0000}, //DFECRU_DFEMODE (0n_94)
+{0x00, 0x95, 0x0008, 0x0000}, //DFECRU_RATESEL (0n_95)
+{0x00, 0x97, 0x0000, 0x8080}, //OUTDRVCTRL (0n_97)
+{0x00, 0x99, 0x001E, 0x0014}, //KR_MAINTAP (0n_99)
+{0x00, 0x9A, 0x000B, 0x0000}, //KR_PRETAP (0n_9A)
+{0x00, 0x9B, 0x0010, 0x0000}, //KR_POSTTAP (0n_9B)
+{0x00, 0x9E, 0x03E8, 0x07D0}, //LOSASSRT (0n_9E)
+{0x00, 0x9F, 0x04EA, 0x09D5}, //LOSDASSRT (0n_9F)
+{0x00, 0xB2, 0x0888, 0x0000}, //NA
+
+//CH 1
+{0x01, 0x84, 0x0800, 0x0000},
+{0x01, 0x8A, 0x7000, 0x0400},
+{0x01, 0x8B, 0x4060, 0x0000},
+{0x01, 0x90, 0xDE85, 0x0000},
+{0x01, 0x91, 0x2020, 0x0000},
+{0x01, 0x92, 0x0860, 0x0000},
+{0x01, 0x93, 0x6000, 0x0000},
+{0x01, 0x94, 0x0001, 0x0000},
+{0x01, 0x95, 0x0008, 0x0000},
+{0x01, 0x97, 0x0000, 0x8080},
+{0x01, 0x99, 0x001E, 0x0014},
+{0x01, 0x9A, 0x000B, 0x0000},
+{0x01, 0x9B, 0x0010, 0x0000},
+{0x01, 0x9E, 0x03E8, 0x07D0},
+{0x01, 0x9F, 0x04EA, 0x09D5},
+{0x01, 0xB2, 0x0888, 0x0000},
+
+//POWER_DOWN Channel 2 and 3
+{0x02, 0x8A, 0x0400, 0x0400},
+{0x02, 0xA4, 0x8000, 0x8000},
+{0x03, 0x8A, 0x0400, 0x0400},
+{0x03, 0xA4, 0x8000, 0x8000},
+
+{0x30, 0x80, 0x3453, 0x0000}, //FSYNM_NVAL (3f_80)
+{0x30, 0x81, 0x00F6, 0x0000}, //FSYNFVAL_MSB (3f_81)
+{0x30, 0x82, 0x8800, 0x0000}, //FSYNFVAL_LSB (3f_82)
+{0x30, 0x83, 0x000F, 0x0000}, //FSYNRVAL_MSB (3f_83)
+{0x30, 0x84, 0xB5E0, 0x0000}, //FSYNRVAL_LSB (3f_84)
+{0x30, 0x85, 0x0000, 0x0400}, //FSYNTST (3f_85)
+
+{0x40, 0x80, 0x4C00, 0x0000}, //ANMUXSEL (40_80)
+{0x40, 0x81, 0x4000, 0x0000}, //DGMUXCTRL (40_81)
+{0x40, 0x82, 0x7800, 0xC000}, //RCKINCTRL (40_82)
+{0x40, 0x84, 0x0020, 0x0000}, //CHRCKSEL (40_84)
+
+{-1, 0, 0, 0},
+};
+
+int bdk_vsc7224_modeset(int twsi_id, int unit, int xfi_mode){
+	bdk_node_t node=0;
+	uint8_t dev_addr=0x10 + unit;
+	uint16_t internal_addr=0x7F;
+	uint16_t page=0;
+	int num_bytes=2;
+	int ia_width_bytes=1;
+	uint64_t data=0;
+	int val=0;
+	int ret = 0, r=0;
+	uint16_t reg = 0;
+
+	if(xfi_mode==0){
+	    printf("XFI Mode Retimer\n");
+	}else{
+	    printf("XFI Mode Redriver\n");
+	}
+
+	while(xfiregmap[r].page != -1){
+	    page = xfiregmap[r].page;
+	    reg  = xfiregmap[r].reg;
+	    if(xfi_mode==0){
+		data = xfiregmap[r].retimer;
+	    }else{
+		data = xfiregmap[r].redriver;
+	    }
+	    ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, (uint64_t)page);
+	    if(ret !=0){
+		printf("XFI init Error\n");
+		break;
+	    }
+	    ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, reg, 	   num_bytes, ia_width_bytes, data);
+	    if(ret !=0){
+		printf("XFI init Error\n");
+		break;
+	    }
+	    val = bdk_twsix_read_ia(node, twsi_id, dev_addr,  reg, num_bytes, ia_width_bytes);
+	    if(val == -1){
+		printf("XFI Read Reg Failed @ page:reg :: %2X:%2X \n",page, reg);
+		break;
+	    }else{
+		xfi_printf(" Page: reg: data: val :: %2X:%2X:%04X:%04X\n", page, reg, (uint16_t)data, val);
+	    }
+	    r++;
+	}
+
+	return ret;
+}
+
+
+int bdk_vsc7224_regmap_modeget(int twsi_id, int unit){
+	bdk_node_t node=0;
+	uint8_t dev_addr=0x10 + unit;
+	uint16_t internal_addr=0x7F;
+	uint16_t page=0;
+	int num_bytes=2;
+	int ia_width_bytes=1;
+	//uint64_t data=0;
+	uint16_t reg = 0;
+	int ret = 0, r=0;
+	int data;
+
+	printf("\n===========================================\n");
+	printf("Page  :Reg    :Value  :Retimer :Redriver\n");
+	printf("===========================================\n");
+	while(xfiregmap[r].page != -1){
+	    page = xfiregmap[r].page;
+	    reg  = xfiregmap[r].reg;
+
+	    ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, (uint64_t)page);
+	    if(ret !=0){
+		printf("XFI init Error\n");
+		break;
+	    }
+	    data = bdk_twsix_read_ia(node, twsi_id, dev_addr,  reg, num_bytes, ia_width_bytes);
+	    if(data == -1){
+		printf("XFI Read Reg Failed @ page:reg :: %2X:%2X \n",page, reg);
+		break;
+	    }
+	    printf("     %02X:     %02X:   %04X:    %04X:    %04X\n", page, reg, (uint16_t)data, xfiregmap[r].retimer, xfiregmap[r].redriver);
+	    r++;
+	}
+	printf("=======================================\n");
+
+	return ret;
+}
+
+int bdk_vsc7224_wp_regs(int twsi_id, int unit, int xfi_wp){
+	bdk_node_t node=0;
+	uint8_t dev_addr=0x10 + unit;
+	uint16_t internal_addr=0x7E;
+	uint16_t data=0x0000;
+	int num_bytes=2;
+	int ia_width_bytes=1;
+	int ret =0;
+
+	if(xfi_wp == 1){
+		data = 0x0000;
+	}else{
+		data = 0xFFFF;
+	}
+
+	ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, (uint64_t)data);
+	if(ret !=0){
+		printf("XFI VSC7224  Write Protect Error\n");
+	}
+
+	return ret;
+}
+
+int bdk_vsc7224_set_reg(int twsi_id, int unit, int page, int reg, int val){
+	bdk_node_t node=0;
+	uint8_t dev_addr=0x10 + unit;
+	uint16_t internal_addr = reg;
+	int num_bytes=2;
+	int ia_width_bytes=1;
+	int ret=0;
+
+	xfi_printf(" Unit: Page: reg: val :: %02x:%2X:%2X:%04X\n", unit, page, reg, val & 0xFFFF);
+	ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, 0x7F, num_bytes, ia_width_bytes, (uint64_t)(page & 0xFF));
+	if (ret) {
+		printf("XFI VSC7224  TWSI Set Page Register Error\n");
+	}
+
+	ret = bdk_twsix_write_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, (uint64_t)(val & 0xFFFF));
+	if (ret) {
+		printf("XFI VSC7224  TWSI Set Register Error\n");
+	}
+
+	return ret;
+}
+
+int bdk_vsc7224_debug(int _debug){
+	debug =_debug;
+	return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse.c
new file mode 100644
index 0000000..e1ef6b0
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy-vetesse.c
@@ -0,0 +1,372 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-hal/if/bdk-if.h>
+#include <libbdk-hal/bdk-mdio.h>
+#include <libbdk-hal/bdk-qlm.h>
+
+static bool LOOP_INTERNAL = false;
+static bool LOOP_EXTERNAL = false;
+
+static uint8_t patch_arr[] = {
+    0x44, 0x83, 0x02, 0x42, 0x12, 0x02, 0x44, 0x93, 0x02, 0x44,
+    0xca, 0x02, 0x44, 0x4d, 0x02, 0x43, 0xef, 0xed, 0xff, 0xe5,
+    0xfc, 0x54, 0x38, 0x64, 0x20, 0x70, 0x08, 0x65, 0xff, 0x70,
+    0x04, 0xed, 0x44, 0x80, 0xff, 0x22, 0x8f, 0x19, 0x7b, 0xbb,
+    0x7d, 0x0e, 0x7f, 0x04, 0x12, 0x3d, 0xd7, 0xef, 0x4e, 0x60,
+    0x03, 0x02, 0x41, 0xf9, 0xe4, 0xf5, 0x1a, 0x74, 0x01, 0x7e,
+    0x00, 0xa8, 0x1a, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33,
+    0xce, 0xd8, 0xf9, 0xff, 0xef, 0x55, 0x19, 0x70, 0x03, 0x02,
+    0x41, 0xed, 0x85, 0x1a, 0xfb, 0x7b, 0xbb, 0xe4, 0xfd, 0xff,
+    0x12, 0x3d, 0xd7, 0xef, 0x4e, 0x60, 0x03, 0x02, 0x41, 0xed,
+    0xe5, 0x1a, 0x54, 0x02, 0x75, 0x1d, 0x00, 0x25, 0xe0, 0x25,
+    0xe0, 0xf5, 0x1c, 0xe4, 0x78, 0xc5, 0xf6, 0xd2, 0x02, 0x12,
+    0x41, 0xfa, 0x7b, 0xff, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3d,
+    0xd7, 0xef, 0x4e, 0x60, 0x03, 0x02, 0x41, 0xe7, 0xc2, 0x02,
+    0x74, 0xc7, 0x25, 0x1a, 0xf9, 0x74, 0xe7, 0x25, 0x1a, 0xf8,
+    0xe6, 0x27, 0xf5, 0x1b, 0xe5, 0x1d, 0x24, 0x5b, 0x12, 0x44,
+    0x2a, 0x12, 0x3e, 0xda, 0x7b, 0xfc, 0x7d, 0x11, 0x7f, 0x07,
+    0x12, 0x3d, 0xd7, 0x78, 0xcc, 0xef, 0xf6, 0x78, 0xc1, 0xe6,
+    0xfe, 0xef, 0xd3, 0x9e, 0x40, 0x06, 0x78, 0xcc, 0xe6, 0x78,
+    0xc1, 0xf6, 0x12, 0x41, 0xfa, 0x7b, 0xec, 0x7d, 0x12, 0x7f,
+    0x07, 0x12, 0x3d, 0xd7, 0x78, 0xcb, 0xef, 0xf6, 0xbf, 0x07,
+    0x06, 0x78, 0xc3, 0x76, 0x1a, 0x80, 0x1f, 0x78, 0xc5, 0xe6,
+    0xff, 0x60, 0x0f, 0xc3, 0xe5, 0x1b, 0x9f, 0xff, 0x78, 0xcb,
+    0xe6, 0x85, 0x1b, 0xf0, 0xa4, 0x2f, 0x80, 0x07, 0x78, 0xcb,
+    0xe6, 0x85, 0x1b, 0xf0, 0xa4, 0x78, 0xc3, 0xf6, 0xe4, 0x78,
+    0xc2, 0xf6, 0x78, 0xc2, 0xe6, 0xff, 0xc3, 0x08, 0x96, 0x40,
+    0x03, 0x02, 0x41, 0xd1, 0xef, 0x54, 0x03, 0x60, 0x33, 0x14,
+    0x60, 0x46, 0x24, 0xfe, 0x60, 0x42, 0x04, 0x70, 0x4b, 0xef,
+    0x24, 0x02, 0xff, 0xe4, 0x33, 0xfe, 0xef, 0x78, 0x02, 0xce,
+    0xa2, 0xe7, 0x13, 0xce, 0x13, 0xd8, 0xf8, 0xff, 0xe5, 0x1d,
+    0x24, 0x5c, 0xcd, 0xe5, 0x1c, 0x34, 0xf0, 0xcd, 0x2f, 0xff,
+    0xed, 0x3e, 0xfe, 0x12, 0x44, 0x6a, 0x7d, 0x11, 0x80, 0x0b,
+    0x78, 0xc2, 0xe6, 0x70, 0x04, 0x7d, 0x11, 0x80, 0x02, 0x7d,
+    0x12, 0x7f, 0x07, 0x12, 0x3e, 0x9a, 0x8e, 0x1e, 0x8f, 0x1f,
+    0x80, 0x03, 0xe5, 0x1e, 0xff, 0x78, 0xc5, 0xe6, 0x06, 0x24,
+    0xcd, 0xf8, 0xa6, 0x07, 0x78, 0xc2, 0x06, 0xe6, 0xb4, 0x1a,
+    0x0a, 0xe5, 0x1d, 0x24, 0x5c, 0x12, 0x44, 0x2a, 0x12, 0x3e,
+    0xda, 0x78, 0xc5, 0xe6, 0x65, 0x1b, 0x70, 0x82, 0x75, 0xdb,
+    0x20, 0x75, 0xdb, 0x28, 0x12, 0x44, 0x42, 0x12, 0x44, 0x42,
+    0xe5, 0x1a, 0x12, 0x44, 0x35, 0xe5, 0x1a, 0xc3, 0x13, 0x12,
+    0x44, 0x35, 0x78, 0xc5, 0x16, 0xe6, 0x24, 0xcd, 0xf8, 0xe6,
+    0xff, 0x7e, 0x08, 0x1e, 0xef, 0xa8, 0x06, 0x08, 0x80, 0x02,
+    0xc3, 0x13, 0xd8, 0xfc, 0xfd, 0xc4, 0x33, 0x54, 0xe0, 0xf5,
+    0xdb, 0xef, 0xa8, 0x06, 0x08, 0x80, 0x02, 0xc3, 0x13, 0xd8,
+    0xfc, 0xfd, 0xc4, 0x33, 0x54, 0xe0, 0x44, 0x08, 0xf5, 0xdb,
+    0xee, 0x70, 0xd8, 0x78, 0xc5, 0xe6, 0x70, 0xc8, 0x75, 0xdb,
+    0x10, 0x02, 0x40, 0xfd, 0x78, 0xc2, 0xe6, 0xc3, 0x94, 0x17,
+    0x50, 0x0e, 0xe5, 0x1d, 0x24, 0x62, 0x12, 0x42, 0x08, 0xe5,
+    0x1d, 0x24, 0x5c, 0x12, 0x42, 0x08, 0x20, 0x02, 0x03, 0x02,
+    0x40, 0x76, 0x05, 0x1a, 0xe5, 0x1a, 0xc3, 0x94, 0x04, 0x50,
+    0x03, 0x02, 0x40, 0x3a, 0x22, 0xe5, 0x1d, 0x24, 0x5c, 0xff,
+    0xe5, 0x1c, 0x34, 0xf0, 0xfe, 0x12, 0x44, 0x6a, 0x22, 0xff,
+    0xe5, 0x1c, 0x34, 0xf0, 0xfe, 0x12, 0x44, 0x6a, 0x22, 0xd2,
+    0x00, 0x75, 0xfb, 0x03, 0xab, 0x7e, 0xaa, 0x7d, 0x7d, 0x19,
+    0x7f, 0x03, 0x12, 0x3e, 0xda, 0xe5, 0x7e, 0x54, 0x0f, 0x24,
+    0xf3, 0x60, 0x03, 0x02, 0x42, 0xb9, 0x12, 0x44, 0xa3, 0x12,
+    0x44, 0xaa, 0xd8, 0xfb, 0xff, 0x20, 0xe2, 0x2a, 0x13, 0x92,
+    0x04, 0xef, 0xa2, 0xe1, 0x92, 0x03, 0x30, 0x04, 0x1f, 0xe4,
+    0xf5, 0x10, 0xe5, 0x10, 0x24, 0x17, 0xfd, 0x7b, 0x54, 0x7f,
+    0x04, 0x12, 0x3d, 0xd7, 0x74, 0x25, 0x25, 0x10, 0xf8, 0xa6,
+    0x07, 0x05, 0x10, 0xe5, 0x10, 0xc3, 0x94, 0x02, 0x40, 0xe4,
+    0x12, 0x44, 0xa3, 0x12, 0x44, 0xaa, 0xd8, 0xfb, 0x54, 0x05,
+    0x64, 0x04, 0x70, 0x27, 0x78, 0xc4, 0xe6, 0x78, 0xc6, 0xf6,
+    0xe5, 0x7d, 0xff, 0x33, 0x95, 0xe0, 0xef, 0x54, 0x0f, 0x78,
+    0xc4, 0xf6, 0x12, 0x42, 0xcf, 0x20, 0x04, 0x0c, 0x12, 0x44,
+    0xa3, 0x12, 0x44, 0xaa, 0xd8, 0xfb, 0x13, 0x92, 0x05, 0x22,
+    0xc2, 0x05, 0x22, 0x12, 0x44, 0xa3, 0x12, 0x44, 0xaa, 0xd8,
+    0xfb, 0x54, 0x05, 0x64, 0x05, 0x70, 0x1e, 0x78, 0xc4, 0x7d,
+    0xb8, 0x12, 0x42, 0xc5, 0x78, 0xc1, 0x7d, 0x74, 0x12, 0x42,
+    0xc5, 0xe4, 0x78, 0xc1, 0xf6, 0x22, 0x7b, 0x01, 0x7a, 0x00,
+    0x7d, 0xee, 0x7f, 0x92, 0x12, 0x38, 0xbd, 0x22, 0xe6, 0xfb,
+    0x7a, 0x00, 0x7f, 0x92, 0x12, 0x38, 0xbd, 0x22, 0x78, 0xc1,
+    0xe6, 0xfb, 0x7a, 0x00, 0x7d, 0x74, 0x7f, 0x92, 0x12, 0x38,
+    0xbd, 0xe4, 0x78, 0xc1, 0xf6, 0xf5, 0x11, 0x74, 0x01, 0x7e,
+    0x00, 0xa8, 0x11, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33,
+    0xce, 0xd8, 0xf9, 0xff, 0x78, 0xc4, 0xe6, 0xfd, 0xef, 0x5d,
+    0x60, 0x44, 0x85, 0x11, 0xfb, 0xe5, 0x11, 0x54, 0x02, 0x25,
+    0xe0, 0x25, 0xe0, 0xfe, 0xe4, 0x24, 0x5b, 0xfb, 0xee, 0x12,
+    0x44, 0x2d, 0x12, 0x3e, 0xda, 0x7b, 0x40, 0x7d, 0x11, 0x7f,
+    0x07, 0x12, 0x3d, 0xd7, 0x74, 0xc7, 0x25, 0x11, 0xf8, 0xa6,
+    0x07, 0x7b, 0x11, 0x7d, 0x12, 0x7f, 0x07, 0x12, 0x3d, 0xd7,
+    0xef, 0x4e, 0x60, 0x09, 0x74, 0xe7, 0x25, 0x11, 0xf8, 0x76,
+    0x04, 0x80, 0x07, 0x74, 0xe7, 0x25, 0x11, 0xf8, 0x76, 0x0a,
+    0x05, 0x11, 0xe5, 0x11, 0xc3, 0x94, 0x04, 0x40, 0x9a, 0x78,
+    0xc6, 0xe6, 0x70, 0x15, 0x78, 0xc4, 0xe6, 0x60, 0x10, 0x75,
+    0xd9, 0x38, 0x75, 0xdb, 0x10, 0x7d, 0xfe, 0x12, 0x43, 0x7d,
+    0x7d, 0x76, 0x12, 0x43, 0x7d, 0x79, 0xc6, 0xe7, 0x78, 0xc4,
+    0x66, 0xff, 0x60, 0x03, 0x12, 0x40, 0x25, 0x78, 0xc4, 0xe6,
+    0x70, 0x09, 0xfb, 0xfa, 0x7d, 0xfe, 0x7f, 0x8e, 0x12, 0x38,
+    0xbd, 0x22, 0x7b, 0x01, 0x7a, 0x00, 0x7f, 0x8e, 0x12, 0x38,
+    0xbd, 0x22, 0xe4, 0xf5, 0x19, 0x74, 0x25, 0x25, 0x19, 0xf8,
+    0xe6, 0x64, 0x03, 0x60, 0x51, 0xe5, 0x19, 0x24, 0x17, 0xfd,
+    0x7b, 0xeb, 0x7f, 0x04, 0x12, 0x3d, 0xd7, 0x8f, 0xfb, 0x7b,
+    0x22, 0x7d, 0x18, 0x7f, 0x06, 0x12, 0x3d, 0xd7, 0xef, 0x64,
+    0x01, 0x4e, 0x60, 0x1c, 0x7d, 0x1c, 0xe4, 0xff, 0x12, 0x3e,
+    0x9a, 0xef, 0x54, 0x1b, 0x64, 0x0a, 0x70, 0x15, 0x7b, 0xcc,
+    0x7d, 0x10, 0xff, 0x12, 0x3d, 0xd7, 0xef, 0x64, 0x01, 0x4e,
+    0x70, 0x07, 0x12, 0x44, 0xb1, 0x7b, 0x03, 0x80, 0x0a, 0x12,
+    0x44, 0xb1, 0x74, 0x25, 0x25, 0x19, 0xf8, 0xe6, 0xfb, 0x7a,
+    0x00, 0x7d, 0x54, 0x12, 0x38, 0xbd, 0x05, 0x19, 0xe5, 0x19,
+    0xc3, 0x94, 0x02, 0x40, 0x9c, 0x22, 0xe5, 0x7e, 0x30, 0xe5,
+    0x35, 0x30, 0xe4, 0x0b, 0x7b, 0x02, 0x7d, 0x33, 0x7f, 0x35,
+    0x12, 0x36, 0x29, 0x80, 0x10, 0x7b, 0x01, 0x7d, 0x33, 0x7f,
+    0x35, 0x12, 0x36, 0x29, 0x90, 0x47, 0xd2, 0xe0, 0x44, 0x04,
+    0xf0, 0x90, 0x47, 0xd2, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x47,
+    0xd1, 0xe0, 0x44, 0x10, 0xf0, 0x7b, 0x05, 0x7d, 0x84, 0x7f,
+    0x86, 0x12, 0x36, 0x29, 0x22, 0xfb, 0xe5, 0x1c, 0x34, 0xf0,
+    0xfa, 0x7d, 0x10, 0x7f, 0x07, 0x22, 0x54, 0x01, 0xc4, 0x33,
+    0x54, 0xe0, 0xf5, 0xdb, 0x44, 0x08, 0xf5, 0xdb, 0x22, 0xf5,
+    0xdb, 0x75, 0xdb, 0x08, 0xf5, 0xdb, 0x75, 0xdb, 0x08, 0x22,
+    0xe5, 0x7e, 0x54, 0x0f, 0x64, 0x01, 0x70, 0x0d, 0xe5, 0x7e,
+    0x30, 0xe4, 0x08, 0x90, 0x47, 0xd0, 0xe0, 0x44, 0x02, 0xf0,
+    0x22, 0x90, 0x47, 0xd0, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0xab,
+    0x07, 0xaa, 0x06, 0x7d, 0x10, 0x7f, 0x07, 0x12, 0x3e, 0xda,
+    0x7b, 0xff, 0x7d, 0x10, 0x7f, 0x07, 0x12, 0x3d, 0xd7, 0xef,
+    0x4e, 0x60, 0xf3, 0x22, 0x12, 0x44, 0xc5, 0x12, 0x44, 0xbb,
+    0x90, 0x47, 0xfa, 0xe0, 0x54, 0xf8, 0x44, 0x02, 0xf0, 0x22,
+    0x30, 0x04, 0x03, 0x12, 0x43, 0x87, 0x78, 0xc4, 0xe6, 0xff,
+    0x60, 0x03, 0x12, 0x40, 0x25, 0x22, 0xe5, 0x7e, 0xae, 0x7d,
+    0x78, 0x04, 0x22, 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0x22,
+    0xe5, 0x19, 0x24, 0x17, 0x54, 0x1f, 0x44, 0x80, 0xff, 0x22,
+    0xe4, 0x78, 0xc4, 0xf6, 0xc2, 0x05, 0x78, 0xc1, 0xf6, 0x22,
+    0xc2, 0x04, 0xc2, 0x03, 0x22, 0x22
+};
+
+/**
+ * Setup Vitesse PHYs
+ * This function sets up one port in a Vitesse VSC8574 for
+ * either SGMII or QSGMII
+ */
+static void setup_vitesse_phy(bdk_node_t node, int mdio_bus, int phy_addr, bool qsgmii)
+{
+    // Select "G" registers
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x10);
+    // Reg 19G, bit 15:14
+    // 0 = SGMII
+    // 1 = QSGMII
+    int reg19 = bdk_mdio_read(node, mdio_bus, phy_addr, 19);
+    int reg18;
+    if (qsgmii)
+    {
+        // QSGMII
+        reg19 = (reg19 & ~(3 << 14)) | (1 << 14);
+        reg18 = 0x80e0;
+    }
+    else
+    {
+        // SGMII
+        reg19 = (reg19 & ~(3 << 14)) | (0 << 14);
+        reg18 = 0x80f0;
+    }
+    bdk_mdio_write(node, mdio_bus, phy_addr, 19, reg19);
+    // Write 18G, change all 4 ports
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, reg18);
+    // Select main registers
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0);
+    // Reg23, 10:8 Select copper
+    int reg23 = bdk_mdio_read(node, mdio_bus, phy_addr, 23);
+    reg23 = (reg23 & ~(7 << 8)) | (0 << 8);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 23, reg23);
+    // Reg0, Reset
+    int reg0 = bdk_mdio_read(node, mdio_bus, phy_addr, 0);
+    reg0 |= (1 << 15);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 0, reg0);
+    // Reg 16E3, bit 7 auto negotiation
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 3);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 16, 0x80);
+    // Select main registers
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0);
+    // Near end loopback (Thunder side)
+    if (LOOP_INTERNAL)
+    {
+        reg0 = bdk_mdio_read(node, mdio_bus, phy_addr, 0);
+        reg0 |= (1 << 14);
+        bdk_mdio_write(node, mdio_bus, phy_addr, 0, reg0);
+    }
+
+    // Far end loopback (External side)
+    if (LOOP_EXTERNAL)
+    {
+        reg23 = bdk_mdio_read(node, mdio_bus, phy_addr, 23);
+        reg23 |= (1 << 3);
+        bdk_mdio_write(node, mdio_bus, phy_addr, 23, reg23);
+    }
+}
+
+static void wr_masked(bdk_node_t node, int mdio_bus, int phy_addr, int reg, int value, int mask)
+{
+    int nmask = ~mask;
+    int old = bdk_mdio_read(node, mdio_bus, phy_addr, reg);
+    int vmask = value & mask;
+    int newv = old & nmask;
+    newv = newv | vmask;
+    bdk_mdio_write(node, mdio_bus, phy_addr, reg, newv);
+}
+
+static void vitesse_program(bdk_node_t node, int mdio_bus, int phy_addr)
+{
+    printf("Programming Vitesse PHY at address %d\n", phy_addr);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0010);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, 0x800f);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0010);
+
+    int reg18g = bdk_mdio_read(node, mdio_bus, phy_addr, 18);
+    int timeout = 10;
+    while ((reg18g & (1<<15)) && (timeout > 0))
+    {
+        bdk_wait_usec(100000);
+        reg18g = bdk_mdio_read(node, mdio_bus, phy_addr, 18);
+        timeout = timeout - 1;
+    }
+    if (timeout == 0)
+        bdk_error("Vetesse: Timeout waiting for complete\n");
+
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0000);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0010);
+    wr_masked(node, mdio_bus, phy_addr, 12, 0x0000, 0x0800);
+    bdk_mdio_write(node, mdio_bus, phy_addr,  9, 0x005b);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 10, 0x005b);
+    wr_masked(node, mdio_bus, phy_addr, 12, 0x0800, 0x0800);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, 0x800f);
+    wr_masked(node, mdio_bus, phy_addr, 0, 0x0000, 0x8000);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, 0x0000);
+    wr_masked(node, mdio_bus, phy_addr, 12, 0x0000, 0x0800);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x10);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 0, 0x7009);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 12, 0x5002);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 11, 0x0000);
+
+    for (unsigned int i=0; i<sizeof(patch_arr); i++)
+    {
+        int d = 0x5000 | patch_arr[i];
+        bdk_mdio_write(node, mdio_bus, phy_addr, 12, d);
+    }
+    bdk_mdio_write(node, mdio_bus, phy_addr, 12, 0x0000);
+
+    bdk_mdio_write(node, mdio_bus, phy_addr,  3, 0x3eb7);
+    bdk_mdio_write(node, mdio_bus, phy_addr,  4, 0x4012);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 12, 0x0100);
+    bdk_mdio_write(node, mdio_bus, phy_addr,  0, 0x4018);
+    bdk_mdio_write(node, mdio_bus, phy_addr,  0, 0xc018);
+
+    // below verifies CRC is correct in 8051 RAM.  CRC is 16-bit.
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0001);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 25, 0x4000);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 26, sizeof(patch_arr) + 1);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0010);
+    bdk_mdio_write(node, mdio_bus, phy_addr, 18, 0x8008);
+
+    reg18g = bdk_mdio_read(node, mdio_bus, phy_addr, 18);
+    timeout = 10;
+    while ((reg18g & (1<<15)) && (timeout > 0))
+    {
+        bdk_wait_usec(100000);
+        reg18g = bdk_mdio_read(node, mdio_bus, phy_addr, 18);
+        timeout = timeout - 1;
+    }
+    if (timeout == 0)
+        bdk_error("Vetesse: Timeout waiting for complete\n");
+
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0001);
+
+    int crc_calculated = bdk_mdio_read(node, mdio_bus, phy_addr, 25);
+    if (crc_calculated != 0xB7C2)
+        printf("8051 crc_calculated = 0x%x, expected_crc = 0x%x\n", crc_calculated, 0xB7C2);
+
+    bdk_mdio_write(node, mdio_bus, phy_addr, 31, 0x0000);
+}
+
+//static void vetesse_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr)
+int bdk_if_phy_vetesse_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr)
+{
+    /* Check if the PHY is Vetesse PHY we expect */
+    int phy_status = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
+    if (phy_status != 0x0007)
+        return 0;
+
+    /* Check that the GSER mode is SGMII or QSGMII */
+    bdk_qlm_modes_t qlm_mode = bdk_qlm_get_mode(node, qlm);
+    if ((qlm_mode != BDK_QLM_MODE_SGMII_1X1) &&
+        (qlm_mode != BDK_QLM_MODE_SGMII_2X1) &&
+        (qlm_mode != BDK_QLM_MODE_SGMII_4X1) &&
+        (qlm_mode != BDK_QLM_MODE_QSGMII_4X1))
+        return 0;
+
+    /* Program the Vetesse PHY */
+    vitesse_program(node, mdio_bus, phy_addr);
+
+    /* Switch the Vitesse PHY to the correct mode */
+    bool is_qsgmii = (qlm_mode == BDK_QLM_MODE_QSGMII_4X1);
+    if (is_qsgmii)
+    {
+        for (int port = 0; port < 4; port++)
+            setup_vitesse_phy(node, mdio_bus, phy_addr + port, true);
+    }
+    else
+        setup_vitesse_phy(node, mdio_bus, phy_addr, false);
+    return 0;
+}
+
+#if 0
+int bdk_if_phy_vetesse_setup(bdk_node_t node)
+{
+    for (int bgx = 0; bgx < 4; bgx++)
+    {
+        int port = 0;
+        int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, port);
+        if (phy_addr != -1)
+        {
+            int node = (phy_addr >> 24) & 0xff;
+            int mdio_bus = (phy_addr >> 8) & 0xff;
+            int mdio_addr = phy_addr & 0xff;
+            if (node == 0xff)
+                node = bdk_numa_local();
+            if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_MDIO)
+            {
+                int qlm = bdk_qlm_get(node, BDK_IF_BGX, bgx, port);
+                if (qlm != -1)
+                    vetesse_setup(node, qlm, mdio_bus, mdio_addr);
+            }
+        }
+    }
+    return 0;
+}
+#endif
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c
new file mode 100644
index 0000000..5b02eff
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c
@@ -0,0 +1,445 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include <libbdk-hal/if/bdk-if.h>
+#include <libbdk-hal/bdk-config.h>
+#include <libbdk-hal/bdk-mdio.h>
+#include <libbdk-hal/bdk-qlm.h>
+#include <libbdk-hal/bdk-twsi.h>
+
+/**
+ * Called when the PHY is connected through TWSI
+ *
+ * @param dev_node Node the ethernet device is on
+ * @param phy_addr Encoded address, see bdk-if.h for format
+ *
+ * @return Link status
+ */
+static bdk_if_link_t __bdk_if_phy_get_twsi(bdk_node_t dev_node, int phy_addr)
+{
+    /* For TWSI:
+        Bits[31:24]: Node ID, 0xff for device node
+        Bits[23:16]: TWSI internal address width in bytes (0-2)
+        Bits[15:12]: 2=TWSI
+        Bits[11:8]: TWSI bus number
+        Bits[7:0]: TWSI address */
+    int node = (phy_addr >> 24) & 0xff;
+    int twsi_ia_width = (phy_addr >> 16) & 0xff;
+    int twsi_bus = (phy_addr >> 8) & 0xf;
+    int twsi_addr = phy_addr & 0xff;
+    if (node == 0xff)
+        node = dev_node;
+
+    bdk_if_link_t result;
+    result.u64 = 0;
+
+    /* This is from the Avago SFP 1G Module data sheet
+       Register 17 (Extended Status 1) */
+    int64_t phy_status = bdk_twsix_read_ia(node, twsi_bus, twsi_addr, 17, 2, twsi_ia_width);
+    if (phy_status != -1)
+    {
+        int speed = (phy_status >> 14)& 3;
+        int duplex = (phy_status >> 13)& 1;
+        int resolved = (phy_status >> 11)& 1;
+        int link = (phy_status >> 10)& 1;
+        if (resolved)
+        {
+            result.s.up = link;
+            result.s.full_duplex = duplex;
+            switch (speed)
+            {
+                case 0: /* 10 Mbps */
+                    result.s.speed = 10;
+                    break;
+                case 1: /* 100 Mbps */
+                    result.s.speed = 100;
+                    break;
+                case 2: /* 1 Gbps */
+                    result.s.speed = 1000;
+                    break;
+                case 3: /* Illegal */
+                    result.u64 = 0;
+                    break;
+            }
+        }
+    }
+
+    return result;
+}
+
+/**
+ * Read the status of a PHY
+ *
+ * @param dev_node Node the ethernet device is on
+ * @param phy_addr Encoded PHY address, see bdk-if.h for format
+ *
+ * @return Link status
+ */
+bdk_if_link_t __bdk_if_phy_get(bdk_node_t dev_node, int phy_addr)
+{
+    int node = (phy_addr >> 24) & 0xff;
+    int mdio_bus = (phy_addr >> 8) & 0xff;
+    int mdio_addr = phy_addr & 0xff;
+    if (node == 0xff)
+        node = dev_node;
+    int phy_status;
+    bdk_if_link_t result;
+    result.u64 = 0;
+
+    /* PHY address of -1 menas there is no PHY and we should have never
+        gotten here */
+    if (phy_addr == -1)
+        return result;
+
+    /* A PHY address with the special value 0x1000 represents a PHY we can't
+        connect to through MDIO which is assumed to be at 1Gbps */
+    if (phy_addr == BDK_IF_PHY_FIXED_1GB)
+    {
+        result.s.up = 1;
+        result.s.full_duplex = 1;
+        result.s.speed = 1000;
+        return result;
+    }
+
+    /* A PHY address with the special value 0x1001 represents a PHY we can't
+        connect to through MDIO which is assumed to be at 100Mbps */
+    if (phy_addr == BDK_IF_PHY_FIXED_100MB)
+    {
+        result.s.up = 1;
+        result.s.full_duplex = 1;
+        result.s.speed = 100;
+        return result;
+    }
+
+    /* Check for a PHY connected through TWSI */
+    if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_TWSI)
+        return __bdk_if_phy_get_twsi(dev_node, phy_addr);
+
+    phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_ID1);
+    if ((phy_status <= 0) || (phy_status == 0xffff))
+        return result;
+
+    switch (phy_status)
+    {
+        case 0x0141: /* Marvell */
+        {
+
+            /* This code assumes we are using a Marvell Gigabit PHY. All the
+                speed information can be read from register 17 in one go. Somebody
+                using a different PHY will need to handle it above in the board
+                specific area */
+            phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17);
+            if (phy_status < 0)
+                return result;
+
+            /* If the resolve bit 11 isn't set, see if autoneg is turned off
+                (bit 12, reg 0). The resolve bit doesn't get set properly when
+                autoneg is off, so force it */
+            if ((phy_status & (1<<11)) == 0)
+            {
+                bdk_mdio_phy_reg_control_t control;
+                int phy_c = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_CONTROL);
+                if (phy_c < 0)
+                    return result;
+                control.u16 = phy_c;
+                if (control.s.autoneg_enable == 0)
+                    phy_status |= 1<<11;
+            }
+
+            /* Only return a link if the PHY has finished auto negotiation
+                and set the resolved bit (bit 11) */
+            if (phy_status & (1<<11))
+            {
+                result.s.up = 1;
+                result.s.full_duplex = ((phy_status>>13)&1);
+                switch ((phy_status>>14)&3)
+                {
+                    case 0: /* 10 Mbps */
+                        result.s.speed = 10;
+                        break;
+                    case 1: /* 100 Mbps */
+                        result.s.speed = 100;
+                        break;
+                    case 2: /* 1 Gbps */
+                        result.s.speed = 1000;
+                        break;
+                    case 3: /* Illegal */
+                        result.u64 = 0;
+                        break;
+                }
+            }
+            break;
+        }
+        case 0x0022: /* Kendin */
+        {
+            /* Register 1Fh - PHY Control */
+            /* Micrel KSZ9031RNX, EBB8104 RGMII transceiver */
+            /* Reports as "Kendin" in BDK_MDIO_PHY_REG_ID1 */
+            phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1F);
+            if (phy_status & (1 << 6)) // Speed Status - 1000Base-T
+            {
+                result.s.up = 1;
+                result.s.speed = 1000;
+            }
+            else if (phy_status & (1 << 5)) // Speed Status - 100Base-TX
+            {
+                result.s.up = 1;
+                result.s.speed = 100;
+            }
+            else if (phy_status & (1 << 4)) // Speed Status - 10Base-T
+            {
+                result.s.up = 1;
+                result.s.speed = 10;
+            }
+            if (phy_status & (1 << 3)) // Duplex Status
+            {
+                result.s.full_duplex = 1;
+            }
+            break;
+        }
+        case 0x0007: /* Vitesse */
+        {
+            /* Auxiliary Control and Status, Address 28 (0x1C) */
+            phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1c);
+            result.s.full_duplex = (phy_status>>5)&1;
+            switch ((phy_status>>3) & 3)
+            {
+                case 0:
+                    result.s.speed = 10;
+                    result.s.up = 1;
+                    break;
+                case 1:
+                    result.s.speed = 100;
+                    result.s.up = 1;
+                    break;
+                default:
+                    result.s.speed = 1000;
+                    break;
+            }
+            phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x01);
+            result.s.up = (phy_status>>2)&1;
+            break;
+        }
+        default: /* Treat like Broadcom */
+        {
+            /* Below we are going to read SMI/MDIO register 0x19 which works
+                on Broadcom parts */
+            phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x19);
+            if (phy_status < 0)
+                return result;
+
+            switch ((phy_status>>8) & 0x7)
+            {
+                case 0:
+                    result.u64 = 0;
+                    break;
+                case 1:
+                    result.s.up = 1;
+                    result.s.full_duplex = 0;
+                    result.s.speed = 10;
+                    break;
+                case 2:
+                    result.s.up = 1;
+                    result.s.full_duplex = 1;
+                    result.s.speed = 10;
+                    break;
+                case 3:
+                    result.s.up = 1;
+                    result.s.full_duplex = 0;
+                    result.s.speed = 100;
+                    break;
+                case 4:
+                    result.s.up = 1;
+                    result.s.full_duplex = 1;
+                    result.s.speed = 100;
+                    break;
+                case 5:
+                    result.s.up = 1;
+                    result.s.full_duplex = 1;
+                    result.s.speed = 100;
+                    break;
+                case 6:
+                    result.s.up = 1;
+                    result.s.full_duplex = 0;
+                    result.s.speed = 1000;
+                    break;
+                case 7:
+                    result.s.up = 1;
+                    result.s.full_duplex = 1;
+                    result.s.speed = 1000;
+                    break;
+            }
+            break;
+        }
+    }
+
+    /* If link is down, return all fields as zero. */
+    if (!result.s.up)
+        result.u64 = 0;
+
+    return result;
+}
+
+/**
+ * PHY XS initialization, primarily for RXAUI
+ *
+ * @param dev_node Node the ethernet device is on
+ * @param phy_addr Encoded PHY address, see bdk-if.h for format
+ *
+ * @return none
+ */
+void __bdk_if_phy_xs_init(bdk_node_t dev_node, int phy_addr)
+{
+    /* This code only supports PHYs connected through MDIO */
+    if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_MDIO)
+        return;
+
+    int node = (phy_addr >> 24) & 0xff;
+    int mdio_bus = (phy_addr >> 8) & 0xff;
+    int mdio_addr = phy_addr & 0xff;
+    if (node == 0xff)
+        node = dev_node;
+
+    /* Read the PMA/PMD Device Identifier (1.2, 1.3)
+       OUI is spread across both registers */
+    int dev_addr = 1;
+    int reg_addr = 2;
+    int phy_id1 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
+    if (phy_id1 == -1)
+        return;
+    reg_addr = 3;
+    int phy_id2 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
+    if (phy_id2 == -1)
+        return;
+    int model_number = (phy_id2 >> 4) & 0x3F;
+    int oui = phy_id1;
+    oui <<= 6;
+    oui |= (phy_id2 >> 10) & 0x3F;
+    switch (oui)
+    {
+       case 0x5016:  /* Marvell */
+           if (model_number == 9) /* 88X3140/3120 */
+           {
+               BDK_TRACE(BGX, "N%d.MDIO%d.%d: Performing PHY reset on Marvell RXAUI PHY\n",
+                   node, mdio_bus, mdio_addr);
+               dev_addr = 4;
+               reg_addr = 0;
+               /* Write bit 15, Software Reset, in PHY XS Control 1 (4.0).  On CN78xx,
+                  sometimes the PHY/BGX gets stuck in local fault mode, link never comes up,
+                  and this appears to clear it up.  Haven't seen this on CN81xx or T88,
+                  but the reset seems like cheap insurance. */
+               if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, (1 << 15)))
+               {
+                   bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
+                   return;
+               }
+
+               int reset_pending = 1;
+               while (reset_pending)
+               {
+                   reset_pending = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
+                   reset_pending &= (1 << 15);
+               }
+
+               /* Adjust the RXAUI TX Level for Marvell PHY, per Brendan Metzner
+                  write 5 to register 4.49155 */
+               reg_addr = 49155;
+               if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, 5))
+               {
+                   bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
+                   return;
+               }
+           }
+           break;
+
+       default:  /* Unknown PHY, or no PHY present */
+           break;
+    }
+}
+
+int bdk_if_phy_setup(bdk_node_t dev_node)
+{
+    /* 81xx has only 2 BGX (BGX0-BGX1); BGX2 is RGMII */
+    for (int bgx = 0; bgx < 2; bgx++)
+    {
+        int port = 0;
+        int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, dev_node, bgx, port);
+        if (phy_addr != -1)
+        {
+            int node = (phy_addr >> 24) & 0xff;
+            int mdio_bus = (phy_addr >> 8) & 0xff;
+            int mdio_addr = phy_addr & 0xff;
+            if (node == 0xff)
+                node = bdk_numa_local();
+            if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_MDIO)
+            {
+                int qlm = bdk_qlm_get_qlm_num(node, BDK_IF_BGX, bgx, port);
+                if (qlm == -1)
+                    continue;
+
+                BDK_TRACE(PHY, "N%d.BGX%d.%d: Configuring ...\n", node, bgx, port);
+
+                 /* Check PHY id */
+                int phy_status_1 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
+                int phy_status_2 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID2);
+
+                /* Vitesse */
+                if (phy_status_1 == 0x0007)
+                {
+                    if (phy_status_2 == 0x0670)
+                    {
+                        bdk_if_phy_vsc8514_setup(node, qlm, mdio_bus, mdio_addr);
+                    }
+                    else
+                    {
+                        bdk_if_phy_vetesse_setup(node, qlm, mdio_bus, mdio_addr);
+                    }
+                }
+
+                /* Marvell */
+                else if (phy_status_1 == 0x0141)
+                    bdk_if_phy_marvell_setup(node, qlm, mdio_bus, mdio_addr);
+                else
+                    BDK_TRACE(PHY, "N%d.BGX%d.%d: Unknown PHY %x\n", node, bgx, port, phy_status_1);
+            }
+        }
+    }
+    return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c
new file mode 100644
index 0000000..303b276
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c
@@ -0,0 +1,1003 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017  Cavium Inc. (support@cavium.com). All rights
+* reserved.
+*
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above
+*     copyright notice, this list of conditions and the following
+*     disclaimer in the documentation and/or other materials provided
+*     with the distribution.
+*
+*   * Neither the name of Cavium Inc. nor the names of
+*     its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written
+*     permission.
+*
+* This Software, including technical data, may be subject to U.S. export
+* control laws, including the U.S. Export Administration Act and its
+* associated regulations, and may be subject to export or import
+* regulations in other countries.
+*
+* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
+* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
+* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
+* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
+* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
+* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
+* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
+* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
+* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+***********************license end**************************************/
+#include <bdk.h>
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/qlm/bdk-qlm-common.h"
+#include "libbdk-arch/bdk-csrs-bgx.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-arch/bdk-csrs-sata.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.h"
+#include "libbdk-hal/bdk-gpio.h"
+
+/**
+ * Return the number of QLMs supported for the chip
+ *
+ * @return Number of QLMs
+ */
+int bdk_qlm_get_num(bdk_node_t node)
+{
+    return 4; /* 4 DLM */
+}
+
+/**
+ * Return the number of lanes in a QLM. QLMs normally contain
+ * 4 lanes, except for chips which only have half of a QLM.
+ *
+ * @param qlm    QLM to get lanes number for
+ *
+ * @return Number of lanes on the QLM
+ */
+int bdk_qlm_get_lanes(bdk_node_t node, int qlm)
+{
+
+    if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+        return 1; /* DLM0 and DLM1 are a single lane on CN80XX */
+    else
+        return 2; /* DLMs */
+}
+
+/**
+ * Lookup the hardware QLM number for a given interface type and index. This
+ * function will fail with a fatal error if called on invalid interfaces for
+ * a chip. It returns the QLM number for an interface without checking to
+ * see if the QLM is in the correct mode.
+ *
+ * @param iftype    Interface type
+ * @param interface Interface index number
+ *
+ * @return QLM number. Dies on a fatal error on failure.
+ */
+int bdk_qlm_get_qlm_num(bdk_node_t node, bdk_if_t iftype, int interface, int index)
+{
+    switch (iftype)
+    {
+        case BDK_IF_BGX:
+        {
+            int qlm;
+            switch (interface)
+            {
+                case 0:
+                {
+                    /* This BGX spans two DLMs. The index must be used to
+                       figure out which DLM we are using */
+                    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(0));
+                    if (gserx_cfg.s.bgx)
+                    {
+                        if (gserx_cfg.s.bgx_quad) /* 4 lanes together */
+                            qlm = 0;
+                        else if (gserx_cfg.s.bgx_dual) /* 2 lanes together */
+                            qlm = (index >= 1) ? 1 : 0;
+                        else /* All lanes independent */
+                        {
+                            bdk_qlm_modes_t mode = bdk_qlm_get_mode(node, 0);
+                            if (mode == BDK_QLM_MODE_QSGMII_4X1)
+                                qlm = 0;
+                            else if (mode <= BDK_QLM_MODE_PCIE_1X8)
+                                qlm = 1;
+                            else if (cavium_is_altpkg(CAVIUM_CN81XX))
+                            {
+                                bdk_qlm_modes_t mode1 = bdk_qlm_get_mode(node, 1);
+                                if ((mode1 != BDK_QLM_MODE_QSGMII_4X1) && (index >= 2))
+                                    return -1;
+                                qlm = (index >= 1) ? 1 : 0;
+                            }
+                            else
+                                qlm = (index >= 2) ? 1 : 0;
+                        }
+                    }
+                    else
+                        qlm = 1;
+                    break;
+                }
+                case 1:
+                {
+                    /* This BGX spans two DLMs. The index must be used to
+                       figure out which DLM we are using */
+                    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(2));
+                    if (gserx_cfg.s.bgx)
+                    {
+                        if (gserx_cfg.s.bgx_quad) /* 4 lanes together */
+                            qlm = 2;
+                        else if (gserx_cfg.s.bgx_dual) /* 2 lanes together */
+                            qlm = (index >= 1) ? 3 : 2;
+                        else /* All lanes independent */
+                        {
+                            bdk_qlm_modes_t mode = bdk_qlm_get_mode(node, 2);
+                            if (mode == BDK_QLM_MODE_QSGMII_4X1)
+                                qlm = 2;
+                            else if (mode <= BDK_QLM_MODE_PCIE_1X8)
+                                qlm = 1;
+                            else
+                                qlm = (index >= 2) ? 3 : 2;
+                        }
+                    }
+                    else
+                        qlm = 3;
+                    break;
+                }
+                default:
+                    return -1;
+            }
+            /* Make sure the QLM is powered up and out of reset */
+            BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
+            if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset)
+                return -1;
+            /* Make sure the QLM is in BGX mode */
+            BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+            if (gserx_cfg.s.bgx)
+                return qlm;
+            else
+                return -1;
+        }
+        case BDK_IF_PCIE: /* PCIe */
+        {
+            switch (interface)
+            {
+                case 0: /* PEM0 */
+                {
+                    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(0));
+                    if (gserx_cfg.s.pcie)
+                        return 0; /* PEM0 is on DLM0 */
+                    else
+                        return -1; /* PEM0 is disabled */
+                }
+                case 1: /* PEM1 */
+                {
+                    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(2));
+                    if (gserx_cfg.s.pcie)
+                        return 2; /* PEM1 is on DLM2 */
+                    else
+                        return -1; /* PEM1 is disabled */
+                }
+                case 2: /* PEM2 */
+                {
+                    BDK_CSR_INIT(pem1_cfg, node, BDK_PEMX_CFG(1));
+                    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(3));
+                    if (!pem1_cfg.cn81xx.lanes4 && gserx_cfg.s.pcie)
+                        return 3; /* PEM2 is on DLM3 */
+                    else
+                        return -1; /* PEM2 is disabled */
+                }
+                default: /* Max of 3 PEMs, 0-2 */
+                    return -1;
+            }
+        }
+        default: /* Not supported by CN81XX */
+            return -1;
+    }
+}
+
+/**
+ * Get the mode of a QLM as a human readable string
+ *
+ * @param qlm    QLM to examine
+ *
+ * @return String mode
+ */
+bdk_qlm_modes_t bdk_qlm_get_mode(bdk_node_t node, int qlm)
+{
+    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+    if (gserx_cfg.s.pcie)
+    {
+        switch (qlm)
+        {
+            case 0: /* PEM0 */
+            {
+                BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(0));
+                if (cavium_is_altpkg(CAVIUM_CN81XX))
+                    return BDK_QLM_MODE_PCIE_1X1; /* PEM0 x1 */
+                else if (pemx_cfg.cn81xx.lanes4)
+                    return BDK_QLM_MODE_PCIE_1X4; /* PEM0 x4 */
+                else
+                    return BDK_QLM_MODE_PCIE_1X2; /* PEM0 x2 */
+            }
+            case 1: /* PEM0 second two lanes */
+                return BDK_QLM_MODE_PCIE_1X4; /* PEM0 x4 */
+            case 2: /* Either PEM1 x4 or PEM1 x2 */
+            {
+                BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(1));
+                if (pemx_cfg.cn81xx.lanes4)
+                    return BDK_QLM_MODE_PCIE_1X4; /* PEM1 x4 */
+                else
+                    return BDK_QLM_MODE_PCIE_1X2; /* PEM1 x2 */
+            }
+            case 3: /* Either PEM1 x4 or PEM2 x2 */
+            {
+                /* Can be last 2 lanes of PEM1 */
+                BDK_CSR_INIT(pem1_cfg, node, BDK_PEMX_CFG(1));
+                if (pem1_cfg.cn81xx.lanes4)
+                    return BDK_QLM_MODE_PCIE_1X4; /* PEM1 x4 */
+                /* Can be 2 lanes of PEM2 */
+                return BDK_QLM_MODE_PCIE_1X2; /* PEM2 x2 */
+            }
+            default:
+                return BDK_QLM_MODE_DISABLED;
+        }
+    }
+    else if (gserx_cfg.s.bgx)
+    {
+        int bgx;
+        int bgx_index;
+        switch (qlm)
+        {
+            case 0:
+            {
+                bgx = 0;
+                bgx_index = 0;
+                break;
+            }
+            case 1:
+                bgx = 0;
+                bgx_index = 2;
+                break;
+            case 2:
+            {
+                bgx = 1;
+                bgx_index = 0;
+                break;
+            }
+            case 3:
+                bgx = 1;
+                bgx_index = 2;
+                break;
+            default:
+                return BDK_QLM_MODE_DISABLED;
+        }
+        BDK_CSR_INIT(cmrx_config, node, BDK_BGXX_CMRX_CONFIG(bgx, bgx_index));
+        bool is_kr = __bdk_qlm_is_lane_kr(node, qlm, 0);
+        switch (cmrx_config.s.lmac_type)
+        {
+            case BDK_BGX_LMAC_TYPES_E_SGMII:
+                if (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2))
+                    return BDK_QLM_MODE_SGMII_1X1;
+                else
+                    return BDK_QLM_MODE_SGMII_2X1;
+            case BDK_BGX_LMAC_TYPES_E_XAUI: return BDK_QLM_MODE_XAUI_1X4; /* Doesn't differntiate between XAUI and DXAUI */
+            case BDK_BGX_LMAC_TYPES_E_RXAUI: return BDK_QLM_MODE_RXAUI_1X2;
+            case BDK_BGX_LMAC_TYPES_E_TENG_R:
+                if (is_kr)
+                    return (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2)) ? BDK_QLM_MODE_10G_KR_1X1 : BDK_QLM_MODE_10G_KR_2X1;
+                else
+                    return (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_XFI_2X1;
+            case BDK_BGX_LMAC_TYPES_E_FORTYG_R:
+                if (is_kr)
+                    return BDK_QLM_MODE_40G_KR4_1X4;
+                else
+                    return BDK_QLM_MODE_XLAUI_1X4;
+            case BDK_BGX_LMAC_TYPES_E_QSGMII: return BDK_QLM_MODE_QSGMII_4X1;
+            default:  return BDK_QLM_MODE_DISABLED;
+        }
+    }
+    else if (gserx_cfg.s.sata)
+        return BDK_QLM_MODE_SATA_2X1;
+    else
+        return BDK_QLM_MODE_DISABLED;
+}
+
+static int qlm_set_sata(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz, bdk_qlm_mode_flags_t flags)
+{
+    /* SATA has a fixed mapping for ports on CN81XX */
+    int sata_port;
+    switch (qlm)
+    {
+        case 3: /* SATA 0-1 = DLM3 lanes 0-1 */
+            sata_port = 0;
+            break;
+        default:
+            bdk_error("Attempted to configure SATA on QLM that doesn't support it\n");
+            return -1;
+    }
+    return __bdk_qlm_set_sata_cn8xxx(node, qlm, baud_mhz, sata_port, sata_port + 1);
+}
+
+/**
+ * For chips that don't use pin strapping, this function programs
+ * the QLM to the specified mode
+ *
+ * @param node     Node to use in a Numa setup
+ * @param qlm      QLM to configure
+ * @param mode     Desired mode
+ * @param baud_mhz Desired speed
+ * @param flags    Flags to specify mode specific options
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_set_mode(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz, bdk_qlm_mode_flags_t flags)
+{
+    int lane_mode = 0xf;
+    int lmac_type = -1;
+    int is_pcie = 0;
+    int is_sata = 0;
+    int is_ilk = 0;
+    int is_bgx = 0;
+    int bgx_block;
+    int bgx_index;
+
+    switch (qlm)
+    {
+        case 0:
+            bgx_block = 0;
+            bgx_index = 0;
+            break;
+        case 1:
+            bgx_block = 0;
+            bgx_index = 2;
+            break;
+        case 2:
+            bgx_block = 1;
+            bgx_index = 0;
+            break;
+        case 3:
+            bgx_block = 1;
+            bgx_index = 2;
+            break;
+        default:
+            bgx_block = -1;
+            bgx_index = -1;
+            break;
+    }
+
+    int measured_ref = bdk_qlm_measure_clock(node, qlm);
+    int ref_clk = (mode == BDK_QLM_MODE_DISABLED) ? 0 : __bdk_qlm_round_refclock(node, qlm, measured_ref);
+    int kr_mode = 0;
+
+    switch (mode)
+    {
+        case BDK_QLM_MODE_PCIE_1X1:
+        case BDK_QLM_MODE_PCIE_1X2:
+        case BDK_QLM_MODE_PCIE_1X4:
+        {
+            /* Note: PCIe ignores baud_mhz. Use the GEN 1/2/3 flags
+               to control speed */
+            is_pcie = 1;
+            if (ref_clk == REF_100MHZ)
+            {
+                BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+                    c.s.pcie_refclk125 = 0);
+                if (baud_mhz == 2500)
+                    lane_mode = BDK_GSER_LMODE_E_R_25G_REFCLK100;
+                else if (baud_mhz == 5000)
+                    lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK100;
+                else
+                    lane_mode = BDK_GSER_LMODE_E_R_8G_REFCLK100;
+            }
+            else if (ref_clk == REF_125MHZ)
+            {
+                BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+                    c.s.pcie_refclk125 = 1);
+                if (baud_mhz == 2500)
+                    lane_mode = BDK_GSER_LMODE_E_R_25G_REFCLK125;
+                else if (baud_mhz == 5000)
+                    lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK125;
+                else
+                    lane_mode = BDK_GSER_LMODE_E_R_8G_REFCLK125;
+            }
+            else
+            {
+                bdk_error("Invalid reference clock for PCIe on QLM%d\n", qlm);
+                return -1;
+            }
+            int cfg_md;
+            if (baud_mhz == 2500)
+                cfg_md = 0; /* Gen1 Speed */
+            else if (baud_mhz == 5000)
+                cfg_md = 1; /* Gen2 Speed */
+            else
+                cfg_md = 2; /* Gen3 Speed */
+            switch (qlm)
+            {
+                case 0: /* Either PEM0 x4 or PEM0 x2 or PEM0 x1 */
+                    BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(0),
+                        c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+                    __bdk_qlm_setup_pem_reset(node, 0, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                    BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(0),
+                        c.cn81xx.lanes4 = (mode == BDK_QLM_MODE_PCIE_1X4);
+                        //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                        c.cn81xx.md = cfg_md);
+                    break;
+                case 1: /* Second two lanes for PEM0 x4 */
+                    /* PEMX_CFG already setup */
+                    break;
+                case 2: /* Either PEM1 x4 or PEM1 x2 */
+                    BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(1),
+                        c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+                    __bdk_qlm_setup_pem_reset(node, 1, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                    BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(1),
+                        c.cn81xx.lanes4 = (mode == BDK_QLM_MODE_PCIE_1X4);
+                        //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                        c.cn81xx.md = cfg_md);
+                    break;
+                case 3: /* Either PEM1 x4 or PEM2 x2 */
+                    if (mode == BDK_QLM_MODE_PCIE_1X4)
+                    {
+                        /* Last 2 lanes of PEM1 */
+                        /* PEMX_CFG already setup */
+                    }
+                    else
+                    {
+                        /* Two lanes for PEM2 */
+                        BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(2),
+                            c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+                        __bdk_qlm_setup_pem_reset(node, 2, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                        BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(2),
+                            c.cn81xx.lanes4 = 0;
+                            //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+                            c.cn81xx.md = cfg_md);
+                    }
+                    break;
+                default:
+                    return -1;
+            }
+            break;
+        }
+        case BDK_QLM_MODE_SGMII_4X1:
+        case BDK_QLM_MODE_SGMII_2X1:
+        case BDK_QLM_MODE_SGMII_1X1:
+            /* Disable port BGX ports 2-3 on CN80XX */
+            if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+            {
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+            }
+            lmac_type = BDK_BGX_LMAC_TYPES_E_SGMII; /* SGMII */
+            is_bgx = 1;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("SGMII", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            break;
+        case BDK_QLM_MODE_XAUI_1X4:
+            lmac_type = BDK_BGX_LMAC_TYPES_E_XAUI; /* XAUI */
+            is_bgx = 5;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XAUI", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            break;
+        case BDK_QLM_MODE_RXAUI_2X2:
+        case BDK_QLM_MODE_RXAUI_1X2:
+            lmac_type = BDK_BGX_LMAC_TYPES_E_RXAUI; /* RXAUI */
+            is_bgx = 3;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("RXAUI", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            break;
+        case BDK_QLM_MODE_XFI_4X1:
+        case BDK_QLM_MODE_XFI_2X1:
+        case BDK_QLM_MODE_XFI_1X1:
+            /* Disable port BGX ports 2-3 on CN80XX */
+            if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+            {
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+            }
+            lmac_type = BDK_BGX_LMAC_TYPES_E_TENG_R; /* 10G_R */
+            is_bgx = 1;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XFI", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            break;
+        case BDK_QLM_MODE_XLAUI_1X4:
+            lmac_type = BDK_BGX_LMAC_TYPES_E_FORTYG_R; /* 40G_R */
+            is_bgx = 5;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XLAUI", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            break;
+        case BDK_QLM_MODE_10G_KR_4X1:
+        case BDK_QLM_MODE_10G_KR_2X1:
+        case BDK_QLM_MODE_10G_KR_1X1:
+            /* Disable port BGX ports 2-3 on CN80XX */
+            if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+            {
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+                BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+            }
+            lmac_type = BDK_BGX_LMAC_TYPES_E_TENG_R; /* 10G_R */
+            is_bgx = 1;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("10G-KR", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            kr_mode = 1;
+            break;
+        case BDK_QLM_MODE_40G_KR4_1X4:
+            lmac_type = BDK_BGX_LMAC_TYPES_E_FORTYG_R; /* 40G_R */
+            is_bgx = 5;
+            lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("40G-KR", qlm, ref_clk, baud_mhz);
+            if (lane_mode == -1)
+                return -1;
+            kr_mode = 1;
+            break;
+        case BDK_QLM_MODE_QSGMII_4X1:
+            lmac_type = BDK_BGX_LMAC_TYPES_E_QSGMII; /* QSGMII */
+            is_bgx = 1;
+            lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII;
+            break;
+        case BDK_QLM_MODE_SATA_2X1:
+            BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_MODE(qlm), c.s.lmode = BDK_GSER_LMODE_E_R_8G_REFCLK100);
+            /* SATA initialization is different than BGX. Call its init function
+               and skip the rest of this routine */
+            return qlm_set_sata(node, qlm, mode, baud_mhz, flags);
+        case BDK_QLM_MODE_DISABLED:
+            /* Set gser for the interface mode */
+            BDK_CSR_MODIFY(c, node, BDK_GSERX_CFG(qlm),
+                c.u = 0);
+            /* Put the PHY in reset */
+            BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+                c.s.phy_reset = 1);
+            return 0;
+        default:
+            bdk_error("Unsupported QLM mode %d\n", mode);
+            return -1;
+    }
+
+    BDK_TRACE(QLM, "N%u.QLM%u: Power up...\n", node, qlm);
+
+    /* Power up phy, but keep it in reset */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+        c.s.phy_pd = 0;
+        c.s.phy_reset = 1);
+
+    /* Set gser for the interface mode */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_CFG(qlm),
+        c.s.sata = is_sata;
+        c.s.ila = is_ilk;
+        c.s.bgx = is_bgx & 1;
+        c.s.bgx_quad = (is_bgx >> 2) & 1;
+        c.s.bgx_dual = (is_bgx >> 1) & 1;
+        c.s.pcie = is_pcie);
+
+    /* Lane mode */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_MODE(qlm),
+        c.s.lmode = lane_mode);
+
+    /* LMAC type. We only program one port as the full setup is done in BGX */
+    if (lmac_type != -1)
+    {
+        BDK_CSR_MODIFY(c, node, BDK_BGXX_CMRX_CONFIG(bgx_block, bgx_index),
+            c.s.enable = 0;
+            c.s.lmac_type = lmac_type);
+    }
+
+    BDK_TRACE(QLM, "N%u.QLM%u: Deassert reset...\n", node, qlm);
+
+    /* Bring phy out of reset */
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+        c.s.phy_reset = 0);
+
+    /* Wait 1us until the management interface is ready to accept
+       read/write commands.*/
+    bdk_wait_usec(1);
+
+    /* Configure the gser pll */
+    __bdk_qlm_init_mode_table(node, qlm, ref_clk);
+
+    /* Remember which lanes are using KR over BGX */
+    if (is_bgx)
+    {
+        int num_lanes = bdk_qlm_get_lanes(node, qlm);
+        for (int lane = 0; lane < num_lanes; lane++)
+            __bdk_qlm_set_lane_kr(node, qlm, lane, kr_mode);
+    }
+
+    /* Wait for reset to complete and the PLL to lock */
+    if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_PLL_STAT(qlm), pll_lock, ==, 1, 10000))
+    {
+        bdk_error("QLM%d: Timeout waiting for GSERX_PLL_STAT[pll_lock]\n", qlm);
+        return -1;
+    }
+
+    /* PCIe mode doesn't become ready until the PEM block attempts to bring
+       the interface up. Skip this check for PCIe */
+    if (!is_pcie && BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_QLM_STAT(qlm), rst_rdy, ==, 1, 10000))
+    {
+        bdk_error("QLM%d: Timeout waiting for GSERX_QLM_STAT[rst_rdy]\n", qlm);
+        return -1;
+    }
+
+    /* cdrlock will be checked in the BGX */
+
+    /* Errata (GSER-27140) SERDES temperature drift sensitivity in receiver */
+    int channel_loss = bdk_config_get_int(BDK_CONFIG_QLM_CHANNEL_LOSS, node, qlm);
+    __bdk_qlm_errata_gser_27140(node, qlm, baud_mhz, channel_loss);
+
+    /* Apply any custom tuning */
+    __bdk_qlm_tune(node, qlm, mode, baud_mhz);
+
+    /* Some modes require 4 lanes, which spans DLMs. For these modes, we need
+       to setup the second DLM at the same time we setup the first. The second
+       DLM also must use the same reference clock as the first */
+    bool paired_dlm = ((qlm & 1) == 0) && /* We're on the first (even) DLM */
+        ((mode == BDK_QLM_MODE_PCIE_1X4) || /* We're using a 4 lane mode */
+         (mode == BDK_QLM_MODE_XAUI_1X4) ||
+         (mode == BDK_QLM_MODE_XLAUI_1X4) ||
+         (mode == BDK_QLM_MODE_40G_KR4_1X4));
+    if (paired_dlm)
+    {
+        /* Use the same reference clock for the second QLM */
+        BDK_CSR_WRITE(node, BDK_GSERX_REFCLK_SEL(qlm + 1),
+            BDK_CSR_READ(node, BDK_GSERX_REFCLK_SEL(qlm)));
+        return bdk_qlm_set_mode(node, qlm + 1, mode, baud_mhz, flags);
+    }
+
+    return 0;
+}
+
+/**
+ * Get the speed (Gbaud) of the QLM in Mhz.
+ *
+ * @param qlm    QLM to examine
+ *
+ * @return Speed in Mhz
+ */
+int bdk_qlm_get_gbaud_mhz(bdk_node_t node, int qlm)
+{
+    BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+    if (gserx_cfg.u == 0)
+        return 0;
+    if (gserx_cfg.s.pcie)
+    {
+        /* QLMs in PCIe mode ignore LMODE and get their speed from
+           the PEM block that controls them */
+        int pem;
+        switch (qlm)
+        {
+            case 0: /* PEM0 */
+            case 1: /* PEM0 */
+                pem = 0;
+                break;
+            case 2: /* PEM1 */
+                pem = 1;
+                break;
+            case 3: /* PEM1 or PEM2 */
+            {
+                BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(1));
+                if (pemx_cfg.cn81xx.lanes4)
+                    pem = 1;
+                else
+                    pem = 2;
+                break;
+            }
+            default:
+                bdk_fatal("QLM%d: In PCIe mode, which shouldn't happen\n", qlm);
+        }
+        return __bdk_qlm_get_gbaud_mhz_pem(node, pem);
+    }
+    else if (gserx_cfg.s.sata)
+    {
+        int sata;
+        switch (qlm)
+        {
+            case 3:
+                sata = 0;
+                break;
+            default:
+                return 0;
+        }
+        BDK_CSR_INIT(sata_uctl_ctl, node, BDK_SATAX_UCTL_CTL(sata));
+        if (!sata_uctl_ctl.s.a_clk_en)
+            return 0;
+        BDK_CSR_INIT(sctl, node, BDK_SATAX_UAHC_P0_SCTL(sata));
+        switch (sctl.s.spd)
+        {
+            case 1: return 1500;
+            case 2: return 3000;
+            case 3: return 6000;
+            default: return 6000; /* No limit, assume 6G */
+        }
+    }
+    else
+        return __bdk_qlm_get_gbaud_mhz_lmode(node, qlm);
+}
+
+/**
+ * Initialize the QLM layer
+ */
+void bdk_qlm_init(bdk_node_t node)
+{
+    /* Setup how each PEM drives the PERST lines */
+    for (int pem = 0; pem < 3; pem++)
+    {
+        BDK_CSR_INIT(rst_ctlx, node, BDK_RST_CTLX(pem));
+        __bdk_qlm_setup_pem_reset(node, pem, !rst_ctlx.s.host_mode);
+    }
+}
+
+static void __bdk_qlm_sff81xx_set_reference(bdk_node_t node, int qlm, int ref_clk)
+{
+    int use_clock;
+
+    if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) || CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+    {
+        // Common clock 0 is 156MHz
+        // Common clock 1 is 100MHz
+        switch (qlm)
+        {
+        case 0:
+             use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+             break;
+        case 1:
+             if (ref_clk == REF_100MHZ)
+                 use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+             else
+                 use_clock = 2; /* DLM1_REF_CLK of 156MHz */
+             break;
+        case 2:
+        case 3:
+        default:
+            if (ref_clk == REF_100MHZ)
+                use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+            else
+                use_clock = 2; /* DLM1_REF_CLK of 156MHz */
+            break;
+        }
+
+          BDK_TRACE(QLM, "Setting N%d.QLM%d to use ref clock %d\n", node, qlm, use_clock);
+    }
+    else
+    {
+        bdk_error("Update %s for qlm auto config of this chip\n",__FUNCTION__);
+        return;
+    }
+    BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+        c.s.com_clk_sel = (use_clock != 2);
+        c.s.use_com1 = (use_clock == 1));
+}
+
+int bdk_qlm_auto_config(bdk_node_t node)
+{
+    return -1;
+}
+
+/**
+ * For Cavium SFF board, query the DIP switches in GPIO o determine the QLM setup.
+ * Configure the GPIOs to read the DLM settings
+ * SW1.1 -> DLM0_SEL -> GPIO_26
+ * SW1.2 -> DLM1_SEL -> GPIO_25
+ * SW1.3 -> DLM2_SEL -> GPIO_31
+ * SW1.4 -> DLM3_SEL -> GPIO_4
+  *V1.x boards SW3.8 -> QSGMII/XFI SEL ->GPIO_9
+  *V2.x boards SW3.7 -> QSGMII/XFI SEL ->GPIO_36
+*/
+int bdk_qlm_dip_auto_config(bdk_node_t node)
+{
+    bdk_qlm_modes_t dlm_mode[4];
+    int dlm_speed = 0;
+    int use_ref = 0;
+    bdk_qlm_mode_flags_t dlm_flags = 0;
+
+    unsigned int dlm_config, dlm3, dlm2, dlm1, dlm0;
+    uint64_t gpio = 0;
+
+    /* Configure the GPIOs to read the DLM settings */
+    /* SW1.1 -> DLM0_SEL -> GPIO_26 */
+    /* SW1.2 -> DLM1_SEL -> GPIO_25 */
+    /* SW1.3 -> DLM2_SEL -> GPIO_31 */
+    /* SW1.4 -> DLM3_SEL -> GPIO_4  */
+    //V1.x boards /* SW3.8 -> QSGMII/XFI SEL ->GPIO_9 */
+    //V2.x boards /* SW3.7 -> QSGMII/XFI SEL ->GPIO_36 */
+    /* Configure the GPIOs are input */
+    bdk_gpio_initialize(node, 26, 0, 0);
+    bdk_gpio_initialize(node, 25, 0, 0);
+    bdk_gpio_initialize(node, 31, 0, 0);
+    bdk_gpio_initialize(node, 4, 0, 0);
+    bdk_gpio_initialize(node, 36, 0, 0);
+
+
+    /* Read the GPIOs */
+    gpio = bdk_gpio_read(node, 0);
+
+    dlm3 = !!(gpio & (1ULL<<4));
+    dlm2 = !!(gpio & (1ULL<<31));
+    dlm1 = !!(gpio & (1ULL<<25));
+    dlm0 = !!(gpio & (1ULL<<26));
+
+
+    dlm_config = (dlm0<<3)| (dlm1<<2) | (dlm2<<1) | (dlm3);
+
+    BDK_TRACE(QLM, "DLM CONFIG:%d gpio36: %d\n", dlm_config, !!(gpio & (1ULL<<36)));
+
+    switch(dlm_config)
+    {
+    case 0:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 1:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 2:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 3:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 4:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 5:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 6:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 7:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 8:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 9:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 10:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 11:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 12:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 13:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    case 14:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+        break;
+    case 15:
+        dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+        dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+        dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+        dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+        break;
+    default:
+        return -1;
+    }
+
+    for(int dlm = 0; dlm < 4; dlm++)
+    {
+        const char *dlm_mode_str = bdk_qlm_mode_tostring(dlm_mode[dlm]);
+        switch(dlm_mode[dlm])
+        {
+            case  BDK_QLM_MODE_DISABLED:
+                break;
+            case BDK_QLM_MODE_XFI_2X1:
+            case BDK_QLM_MODE_XFI_1X1:
+                use_ref = REF_156MHZ;
+                dlm_speed = 10312;
+                break;
+            case BDK_QLM_MODE_SATA_2X1:
+                dlm_speed = 6000;
+                use_ref = REF_100MHZ;
+                break;
+            case BDK_QLM_MODE_PCIE_1X2:
+            case BDK_QLM_MODE_PCIE_1X4:
+                dlm_speed = 8000;
+                use_ref =REF_100MHZ;
+                break;
+            case BDK_QLM_MODE_QSGMII_4X1:
+                use_ref = REF_100MHZ;
+                dlm_speed = 5000;
+                break;
+            default:
+                bdk_error("Unsupported N%d.QLM%d mode: %s(%d)",
+                          node, dlm,
+                          dlm_mode_str ? dlm_mode_str : "???",
+                          dlm_mode[dlm]);
+                return -1;
+        }
+        if ((1 == dlm) && (dlm_mode[dlm] != BDK_QLM_MODE_QSGMII_4X1) && (dlm_mode[dlm] != BDK_QLM_MODE_DISABLED))
+        {
+        /* This code is specific to sff8104 board
+        ** QSGMII phy is wired to dlm1-gser lane 2
+        ** AQR-107 phy is wired to dlm1-gser lane 3
+        ** bdk always uses bgx0.port0 on that board
+                */
+                // If dlm1 is in XFI mode, change PHY address to mdio of aquantia phy
+                unsigned mdio_bus = 1;
+                unsigned mdio_addr = 0;
+                int phy_cfg = 0xff<<24 | ((mdio_bus& 0xf)<<8) | (mdio_addr & 0xff);
+                bdk_config_set_int((uint32_t) phy_cfg,BDK_CONFIG_PHY_ADDRESS, node, 0, 0);
+        /* Indicate serdes lane 3 , aquantia phy active */
+            int aq_phy = (0x3<<8) | 1;
+                bdk_config_set_int(aq_phy, BDK_CONFIG_AQUANTIA_PHY,node,0,0);
+                BDK_TRACE(QLM,"Disabling phys 0.1,0.2,0.3\n");
+                for (int i = 1; i<4; i++) {
+                    bdk_config_set_int(-1,BDK_CONFIG_PHY_ADDRESS, node, 0, i);
+                    bdk_config_set_int(0,BDK_CONFIG_BGX_ENABLE,node,0,i);
+                }
+        }
+
+        BDK_TRACE(QLM, "Setting N%d.QLM%d mode %s(%d), speed %d, flags 0x%x\n",
+                  node, dlm, dlm_mode_str, dlm_mode[dlm], dlm_speed, dlm_flags);
+
+        /* Set the reference clock for this QLM */
+        __bdk_qlm_sff81xx_set_reference(node, dlm, use_ref);
+
+        if (bdk_qlm_set_mode(node, dlm, dlm_mode[dlm], dlm_speed, dlm_flags))
+            return -1;
+    }
+    return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c b/src/ven