diff --git a/board/traverse/common/Kconfig b/board/traverse/common/Kconfig
new file mode 100644
index 0000000000..d34832bd0d
--- /dev/null
+++ b/board/traverse/common/Kconfig
@@ -0,0 +1,6 @@
+config TEN64_CONTROLLER
+	bool "Enable Ten64 board controller driver"
+	depends on TARGET_TEN64
+	help
+		Support for the board microcontroller on the Traverse
+		Ten64 family of boards.
diff --git a/board/traverse/common/Makefile b/board/traverse/common/Makefile
new file mode 100644
index 0000000000..d31e3535b9
--- /dev/null
+++ b/board/traverse/common/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TEN64_CONTROLLER) += ten64_controller.o
diff --git a/board/traverse/common/ten64-controller.h b/board/traverse/common/ten64-controller.h
new file mode 100644
index 0000000000..fed6af470d
--- /dev/null
+++ b/board/traverse/common/ten64-controller.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef TEN64_CNTRL_H
+#define TEN64_CNTRL_H
+
+/**
+ * struct t64uc_board_info - Board Information Structure
+ * @mac: Base MAC address
+ * @cpuId: Microcontroller unique serial number
+ * @fwversion_major: Microcontroller version number (Major)
+ * @fwversion_minor: Microcontroller version number (Minor)
+ * @fwversion_patch: Microcontroller version number (Patch)
+ */
+struct t64uc_board_info {
+	u8 mac[6];
+	u32 cpuId[4];
+	u8 fwversion_major;
+	u8 fwversion_minor;
+	u8 fwversion_patch;
+} __packed;
+
+enum {
+	TEN64_CNTRL_GET_BOARD_INFO,
+	TEN64_CNTRL_10G_OFF,
+	TEN64_CNTRL_10G_ON,
+	TEN64_CNTRL_SET_NEXT_BOOTSRC
+};
+
+#endif
diff --git a/board/traverse/common/ten64_controller.c b/board/traverse/common/ten64_controller.c
new file mode 100644
index 0000000000..11e068f94f
--- /dev/null
+++ b/board/traverse/common/ten64_controller.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/* Ten64 Board Microcontroller Driver
+ * Copyright 2021 Traverse Technologies Australia
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <misc.h>
+#include <i2c.h>
+#include <hexdump.h>
+#include <dm/device_compat.h>
+#include <inttypes.h>
+#include <linux/delay.h>
+
+#include "ten64-controller.h"
+
+/* Microcontroller command set and structure
+ * These should not be used outside this file
+ */
+
+#define T64_UC_DATA_MAX_SIZE            128U
+#define T64_UC_API_MSG_HEADER_SIZE      4U
+#define T64_UC_API_HEADER_PREAMB        0xcabe
+
+#define TEN64_UC_CMD_SET_BOARD_MAC		0x10
+#define TEN64_UC_CMD_GET_BOARD_INFO		0x11
+
+#define TEN64_UC_CMD_GET_STATE			0x20
+#define TEN64_UC_CMD_SET_RESET_BTN_HOLD_TIME	0x21
+#define TEN64_UC_CMD_ENABLE_RESET_BUTTON	0x22
+#define TEN64_UC_CMD_SET_NEXT_BOOTSRC		0x23
+#define TEN64_UC_CMD_ENABLE_10G 		0x24
+
+#define TEN64_UC_CMD_FWUP_GET_INFO		0xA0
+#define TEN64_UC_CMD_FWUP_INIT			0xA1
+#define TEN64_UC_CMD_FWUP_XFER			0xA2
+#define TEN64_UC_CMD_FWUP_CHECK 		0xA3
+#define TEN64_UC_CMD_FWUPBOOT			0xA
+
+/** struct t64uc_message - Wire Format for microcontroller messages
+ * @preamb: Message preamble (always 0xcabe)
+ * @cmd: Command to invoke
+ * @len: Length of data
+ * @data: Command data, up to 128 bytes
+ */
+struct t64uc_message {
+	u16 preamb;
+	u8 cmd;
+	u8 len;
+	u8 data[T64_UC_DATA_MAX_SIZE];
+}  __packed;
+
+#define	T64_CTRL_IO_SET		1U
+#define	T64_CTRL_IO_CLEAR	2U
+#define	T64_CTRL_IO_TOGGLE	3U
+#define	T64_CTRL_IO_RESET	4U
+#define	T64_CTRL_IO_UNKNOWN	5U
+
+/** struct t64uc_board_10g_enable - Wrapper for 10G enable command
+ * @control: state to set the 10G retimer - either
+ *	     T64_CTRL_IO_CLEAR (0x02) for off or
+ *	     T64_CTRL_IO_SET (0x01) for on.
+ *
+ * This struct exists to simplify the wrapping of the
+ * command value into a microcontroller message and passing into
+ * functions.
+ */
+struct t64uc_board_10g_enable {
+	u8 control;
+} __packed;
+
+/** ten64_controller_send_recv_command() - Wrapper function to
+ * send a command to the microcontroller.
+ * @uc_chip: the DM I2C chip handle for the microcontroller
+ * @uc_cmd: the microcontroller API command code
+ * @uc_cmd_data: pointer to the data struct for this command
+ * @uc_data_len: size of command data struct
+ * @return_data: place to store response from microcontroller, NULL if not expected
+ * @expected_return_len: expected size of microcontroller command response
+ * @return_message_wait: wait this long (in us) before reading the response
+ *
+ * Invoke a microcontroller command and receive a response.
+ * This function includes communicating with the microcontroller over
+ * I2C and encoding a message in the wire format.
+ *
+ * Return: 0 if successful, error code otherwise.
+ * Returns -EBADMSG if the microcontroller response could not be validated,
+ * other error codes may be passed from dm_i2c_xfer()
+ */
+static int ten64_controller_send_recv_command(struct udevice *ucdev, u8 uc_cmd,
+					      void *uc_cmd_data, u8 cmd_data_len,
+					      void *return_data, u8 expected_return_len,
+					      u16 return_message_wait)
+{
+	int ret;
+	struct t64uc_message send, recv;
+	struct i2c_msg command_message, return_message;
+	struct dm_i2c_chip *chip = dev_get_parent_plat(ucdev);
+
+	dev_dbg(ucdev, "%s sending cmd %02X len %d\n", __func__, uc_cmd, cmd_data_len);
+
+	send.preamb = T64_UC_API_HEADER_PREAMB;
+	send.cmd = uc_cmd;
+	send.len = cmd_data_len;
+	if (uc_cmd_data && cmd_data_len > 0)
+		memcpy(send.data, uc_cmd_data, cmd_data_len);
+
+	command_message.addr = chip->chip_addr;
+	command_message.len = T64_UC_API_MSG_HEADER_SIZE + send.len;
+	command_message.buf = (void *)&send;
+	command_message.flags = I2C_M_STOP;
+
+	ret = dm_i2c_xfer(ucdev, &command_message, 1);
+	if (!return_data)
+		return ret;
+
+	udelay(return_message_wait);
+
+	return_message.addr = chip->chip_addr;
+	return_message.len = T64_UC_API_MSG_HEADER_SIZE + expected_return_len;
+	return_message.buf = (void *)&recv;
+	return_message.flags = I2C_M_RD;
+
+	ret = dm_i2c_xfer(ucdev, &return_message, 1);
+	if (ret)
+		return ret;
+
+	if (recv.preamb != T64_UC_API_HEADER_PREAMB) {
+		dev_err(ucdev, "%s: No preamble received in microcontroller response\n",
+			__func__);
+		return -EBADMSG;
+	}
+	if (recv.cmd != uc_cmd) {
+		dev_err(ucdev, "%s: command response mismatch, got %02X expecting %02X\n",
+			__func__, recv.cmd, uc_cmd);
+		return -EBADMSG;
+	}
+	if (recv.len != expected_return_len) {
+		dev_err(ucdev, "%s: received message has unexpected length, got %d expected %d\n",
+			__func__, recv.len, expected_return_len);
+		return -EBADMSG;
+	}
+	memcpy(return_data, recv.data, expected_return_len);
+	return ret;
+}
+
+/** ten64_controller_send_command() - Send command to microcontroller without
+ * expecting a response (for example, invoking a control command)
+ * @uc_chip: the DM I2C chip handle for the microcontroller
+ * @uc_cmd: the microcontroller API command code
+ * @uc_cmd_data: pointer to the data struct for this command
+ * @uc_data_len: size of command data struct
+ */
+static int ten64_controller_send_command(struct udevice *ucdev, u8 uc_cmd,
+					 void *uc_cmd_data, u8 cmd_data_len)
+{
+	return ten64_controller_send_recv_command(ucdev, uc_cmd,
+						  uc_cmd_data, cmd_data_len,
+						  NULL, 0, 0);
+}
+
+/** ten64_controller_get_board_info() - Get board information from microcontroller
+ * @dev: The microcontroller device handle
+ * @out: Pointer to a t64uc_board_info struct that has been allocated by the caller
+ */
+static int ten64_controller_get_board_info(struct udevice *dev, struct t64uc_board_info *out)
+{
+	int ret;
+
+	ret = ten64_controller_send_recv_command(dev, TEN64_UC_CMD_GET_BOARD_INFO,
+						 NULL, 0, out,
+						 sizeof(struct t64uc_board_info),
+						 10000);
+	if (ret) {
+		dev_err(dev, "%s unable to send board info command: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ten64_controller_10g_enable_command() - Sends a 10G (Retimer) enable command
+ * to the microcontroller.
+ * @ucdev: The microcontroller udevice
+ * @value: The value flag for the 10G state
+ */
+static int ten64_controller_10g_enable_command(struct udevice *ucdev, u8 value)
+{
+	int ret;
+	struct t64uc_board_10g_enable enable_msg;
+
+	enable_msg.control = value;
+
+	ret = ten64_controller_send_command(ucdev, TEN64_UC_CMD_ENABLE_10G,
+					    &enable_msg, sizeof(enable_msg));
+	if (ret) {
+		dev_err(ucdev, "ERROR sending uC 10G Enable message: %d\n", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+int ten64_controller_call(struct udevice *dev, int msgid, void *tx_msg, int tx_size,
+			  void *rx_msg, int rx_size)
+{
+	switch (msgid) {
+	case TEN64_CNTRL_GET_BOARD_INFO:
+		return ten64_controller_get_board_info(dev, (struct t64uc_board_info *)rx_msg);
+	case TEN64_CNTRL_10G_OFF:
+		return ten64_controller_10g_enable_command(dev, T64_CTRL_IO_CLEAR);
+	case TEN64_CNTRL_10G_ON:
+		return ten64_controller_10g_enable_command(dev, T64_CTRL_IO_SET);
+	default:
+		dev_err(dev, "%s: Unknown operation %d\n", __func__, msgid);
+	}
+	return -EINVAL;
+}
+
+static struct misc_ops ten64_ctrl_ops  = {
+	.call = ten64_controller_call
+};
+
+static const struct udevice_id ten64_controller_ids[] = {
+	{.compatible = "traverse,ten64-controller"},
+	{}
+};
+
+U_BOOT_DRIVER(ten64_controller) = {
+	.name = "ten64-controller-i2c",
+	.id = UCLASS_MISC,
+	.of_match = ten64_controller_ids,
+	.ops = &ten64_ctrl_ops
+};
