[PATCH 1/2] board: traverse: add Ten64 board controller driver
Mathew McBride
matt at traverse.com.au
Wed Nov 3 06:29:20 CET 2021
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 <matt at 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
More information about the U-Boot
mailing list