summaryrefslogtreecommitdiff
path: root/board/traverse/common/ten64_controller.c
blob: d6ef8a8d0df99a18e7e3f0443ccfefd3b89a1a64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// 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
};