mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-22 19:23:07 +00:00
241 lines
7 KiB
C
241 lines
7 KiB
C
|
// 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
|
||
|
|
||
|
enum {
|
||
|
TEN64_UC_CMD_SET_BOARD_MAC = 0x10,
|
||
|
TEN64_UC_CMD_GET_BOARD_INFO = 0x11,
|
||
|
TEN64_UC_CMD_GET_STATE = 0x20,
|
||
|
TEN64_UC_CMD_SET_RESET_BTN_HOLD_TIME = 0x21,
|
||
|
TEN64_UC_CMD_ENABLE_RESET_BUTTON = 0x22,
|
||
|
TEN64_UC_CMD_SET_NEXT_BOOTSRC = 0x23,
|
||
|
TEN64_UC_CMD_ENABLE_10G = 0x24,
|
||
|
TEN64_UC_CMD_FWUP_GET_INFO = 0xA0,
|
||
|
TEN64_UC_CMD_FWUP_INIT = 0xA1,
|
||
|
TEN64_UC_CMD_FWUP_XFER = 0xA2,
|
||
|
TEN64_UC_CMD_FWUP_CHECK = 0xA3,
|
||
|
TEN64_UC_CMD_FWUPBOOT = 0x0A
|
||
|
};
|
||
|
|
||
|
/** 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;
|
||
|
|
||
|
enum {
|
||
|
T64_CTRL_IO_SET = 1U,
|
||
|
T64_CTRL_IO_CLEAR = 2U,
|
||
|
T64_CTRL_IO_TOGGLE = 3U,
|
||
|
T64_CTRL_IO_RESET = 4U,
|
||
|
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
|
||
|
};
|