Traverse Technologies Ten64 family boards use a microcontroller
to control low level board functions like startup and reset,
as well as holding details such as the board MAC address.

Communication between the CPU and microcontroller is via
I2C.

To keep the driver structure clean between the Ten64 board
file, DM_I2C, and a future utility command, this driver
has been implemented as a misc uclass device.

Signed-off-by: Mathew McBride <m...@traverse.com.au>
---
 board/traverse/common/Kconfig            |   6 +
 board/traverse/common/Makefile           |   1 +
 board/traverse/common/ten64-controller.h |  28 +++
 board/traverse/common/ten64_controller.c | 238 +++++++++++++++++++++++
 4 files changed, 273 insertions(+)
 create mode 100644 board/traverse/common/Kconfig
 create mode 100644 board/traverse/common/Makefile
 create mode 100644 board/traverse/common/ten64-controller.h
 create mode 100644 board/traverse/common/ten64_controller.c

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
+};
-- 
2.30.1

Reply via email to