1 // SPDX-License-Identifier: GPL-2.0+
2 
3 /* Ten64 Board Microcontroller Driver
4  * Copyright 2021 Traverse Technologies Australia
5  *
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <misc.h>
11 #include <i2c.h>
12 #include <hexdump.h>
13 #include <dm/device_compat.h>
14 #include <inttypes.h>
15 #include <linux/delay.h>
16 
17 #include "ten64-controller.h"
18 
19 /* Microcontroller command set and structure
20  * These should not be used outside this file
21  */
22 
23 #define T64_UC_DATA_MAX_SIZE            128U
24 #define T64_UC_API_MSG_HEADER_SIZE      4U
25 #define T64_UC_API_HEADER_PREAMB        0xcabe
26 
27 enum {
28 	TEN64_UC_CMD_SET_BOARD_MAC = 0x10,
29 	TEN64_UC_CMD_GET_BOARD_INFO = 0x11,
30 	TEN64_UC_CMD_GET_STATE = 0x20,
31 	TEN64_UC_CMD_SET_RESET_BTN_HOLD_TIME = 0x21,
32 	TEN64_UC_CMD_ENABLE_RESET_BUTTON = 0x22,
33 	TEN64_UC_CMD_SET_NEXT_BOOTSRC = 0x23,
34 	TEN64_UC_CMD_ENABLE_10G = 0x24,
35 	TEN64_UC_CMD_FWUP_GET_INFO = 0xA0,
36 	TEN64_UC_CMD_FWUP_INIT = 0xA1,
37 	TEN64_UC_CMD_FWUP_XFER = 0xA2,
38 	TEN64_UC_CMD_FWUP_CHECK = 0xA3,
39 	TEN64_UC_CMD_FWUPBOOT = 0x0A
40 };
41 
42 /** struct t64uc_message - Wire Format for microcontroller messages
43  * @preamb: Message preamble (always 0xcabe)
44  * @cmd: Command to invoke
45  * @len: Length of data
46  * @data: Command data, up to 128 bytes
47  */
48 struct t64uc_message {
49 	u16 preamb;
50 	u8 cmd;
51 	u8 len;
52 	u8 data[T64_UC_DATA_MAX_SIZE];
53 }  __packed;
54 
55 enum {
56 	T64_CTRL_IO_SET = 1U,
57 	T64_CTRL_IO_CLEAR = 2U,
58 	T64_CTRL_IO_TOGGLE = 3U,
59 	T64_CTRL_IO_RESET = 4U,
60 	T64_CTRL_IO_UNKNOWN = 5U
61 };
62 
63 /** struct t64uc_board_10g_enable - Wrapper for 10G enable command
64  * @control: state to set the 10G retimer - either
65  *	     T64_CTRL_IO_CLEAR (0x02) for off or
66  *	     T64_CTRL_IO_SET (0x01) for on.
67  *
68  * This struct exists to simplify the wrapping of the
69  * command value into a microcontroller message and passing into
70  * functions.
71  */
72 struct t64uc_board_10g_enable {
73 	u8 control;
74 } __packed;
75 
76 /** ten64_controller_send_recv_command() - Wrapper function to
77  * send a command to the microcontroller.
78  * @uc_chip: the DM I2C chip handle for the microcontroller
79  * @uc_cmd: the microcontroller API command code
80  * @uc_cmd_data: pointer to the data struct for this command
81  * @uc_data_len: size of command data struct
82  * @return_data: place to store response from microcontroller, NULL if not expected
83  * @expected_return_len: expected size of microcontroller command response
84  * @return_message_wait: wait this long (in us) before reading the response
85  *
86  * Invoke a microcontroller command and receive a response.
87  * This function includes communicating with the microcontroller over
88  * I2C and encoding a message in the wire format.
89  *
90  * Return: 0 if successful, error code otherwise.
91  * Returns -EBADMSG if the microcontroller response could not be validated,
92  * other error codes may be passed from dm_i2c_xfer()
93  */
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)94 static int ten64_controller_send_recv_command(struct udevice *ucdev, u8 uc_cmd,
95 					      void *uc_cmd_data, u8 cmd_data_len,
96 					      void *return_data, u8 expected_return_len,
97 					      u16 return_message_wait)
98 {
99 	int ret;
100 	struct t64uc_message send, recv;
101 	struct i2c_msg command_message, return_message;
102 	struct dm_i2c_chip *chip = dev_get_parent_plat(ucdev);
103 
104 	dev_dbg(ucdev, "%s sending cmd %02X len %d\n", __func__, uc_cmd, cmd_data_len);
105 
106 	send.preamb = T64_UC_API_HEADER_PREAMB;
107 	send.cmd = uc_cmd;
108 	send.len = cmd_data_len;
109 	if (uc_cmd_data && cmd_data_len > 0)
110 		memcpy(send.data, uc_cmd_data, cmd_data_len);
111 
112 	command_message.addr = chip->chip_addr;
113 	command_message.len = T64_UC_API_MSG_HEADER_SIZE + send.len;
114 	command_message.buf = (void *)&send;
115 	command_message.flags = I2C_M_STOP;
116 
117 	ret = dm_i2c_xfer(ucdev, &command_message, 1);
118 	if (!return_data)
119 		return ret;
120 
121 	udelay(return_message_wait);
122 
123 	return_message.addr = chip->chip_addr;
124 	return_message.len = T64_UC_API_MSG_HEADER_SIZE + expected_return_len;
125 	return_message.buf = (void *)&recv;
126 	return_message.flags = I2C_M_RD;
127 
128 	ret = dm_i2c_xfer(ucdev, &return_message, 1);
129 	if (ret)
130 		return ret;
131 
132 	if (recv.preamb != T64_UC_API_HEADER_PREAMB) {
133 		dev_err(ucdev, "%s: No preamble received in microcontroller response\n",
134 			__func__);
135 		return -EBADMSG;
136 	}
137 	if (recv.cmd != uc_cmd) {
138 		dev_err(ucdev, "%s: command response mismatch, got %02X expecting %02X\n",
139 			__func__, recv.cmd, uc_cmd);
140 		return -EBADMSG;
141 	}
142 	if (recv.len != expected_return_len) {
143 		dev_err(ucdev, "%s: received message has unexpected length, got %d expected %d\n",
144 			__func__, recv.len, expected_return_len);
145 		return -EBADMSG;
146 	}
147 	memcpy(return_data, recv.data, expected_return_len);
148 	return ret;
149 }
150 
151 /** ten64_controller_send_command() - Send command to microcontroller without
152  * expecting a response (for example, invoking a control command)
153  * @uc_chip: the DM I2C chip handle for the microcontroller
154  * @uc_cmd: the microcontroller API command code
155  * @uc_cmd_data: pointer to the data struct for this command
156  * @uc_data_len: size of command data struct
157  */
ten64_controller_send_command(struct udevice * ucdev,u8 uc_cmd,void * uc_cmd_data,u8 cmd_data_len)158 static int ten64_controller_send_command(struct udevice *ucdev, u8 uc_cmd,
159 					 void *uc_cmd_data, u8 cmd_data_len)
160 {
161 	return ten64_controller_send_recv_command(ucdev, uc_cmd,
162 						  uc_cmd_data, cmd_data_len,
163 						  NULL, 0, 0);
164 }
165 
166 /** ten64_controller_get_board_info() -Get board information from microcontroller
167  * @dev: The microcontroller device handle
168  * @out: Pointer to a t64uc_board_info struct that has been allocated by the caller
169  */
ten64_controller_get_board_info(struct udevice * dev,struct t64uc_board_info * out)170 static int ten64_controller_get_board_info(struct udevice *dev, struct t64uc_board_info *out)
171 {
172 	int ret;
173 
174 	ret = ten64_controller_send_recv_command(dev, TEN64_UC_CMD_GET_BOARD_INFO,
175 						 NULL, 0, out,
176 						 sizeof(struct t64uc_board_info),
177 						 10000);
178 	if (ret) {
179 		dev_err(dev, "%s unable to send board info command: %d\n",
180 			__func__, ret);
181 		return ret;
182 	}
183 
184 	return 0;
185 }
186 
187 /**
188  * ten64_controller_10g_enable_command() - Sends a 10G (Retimer) enable command
189  * to the microcontroller.
190  * @ucdev: The microcontroller udevice
191  * @value: The value flag for the 10G state
192  */
ten64_controller_10g_enable_command(struct udevice * ucdev,u8 value)193 static int ten64_controller_10g_enable_command(struct udevice *ucdev, u8 value)
194 {
195 	int ret;
196 	struct t64uc_board_10g_enable enable_msg;
197 
198 	enable_msg.control = value;
199 
200 	ret = ten64_controller_send_command(ucdev, TEN64_UC_CMD_ENABLE_10G,
201 					    &enable_msg, sizeof(enable_msg));
202 	if (ret) {
203 		dev_err(ucdev, "ERROR sending uC 10G Enable message: %d\n", ret);
204 		return -1;
205 	}
206 
207 	return 0;
208 }
209 
ten64_controller_call(struct udevice * dev,int msgid,void * tx_msg,int tx_size,void * rx_msg,int rx_size)210 int ten64_controller_call(struct udevice *dev, int msgid, void *tx_msg, int tx_size,
211 			  void *rx_msg, int rx_size)
212 {
213 	switch (msgid) {
214 	case TEN64_CNTRL_GET_BOARD_INFO:
215 		return ten64_controller_get_board_info(dev, (struct t64uc_board_info *)rx_msg);
216 	case TEN64_CNTRL_10G_OFF:
217 		return ten64_controller_10g_enable_command(dev, T64_CTRL_IO_CLEAR);
218 	case TEN64_CNTRL_10G_ON:
219 		return ten64_controller_10g_enable_command(dev, T64_CTRL_IO_SET);
220 	default:
221 		dev_err(dev, "%s: Unknown operation %d\n", __func__, msgid);
222 	}
223 	return -EINVAL;
224 }
225 
226 static struct misc_ops ten64_ctrl_ops  = {
227 	.call = ten64_controller_call
228 };
229 
230 static const struct udevice_id ten64_controller_ids[] = {
231 	{.compatible = "traverse,ten64-controller"},
232 	{}
233 };
234 
235 U_BOOT_DRIVER(ten64_controller) = {
236 	.name = "ten64-controller-i2c",
237 	.id = UCLASS_MISC,
238 	.of_match = ten64_controller_ids,
239 	.ops = &ten64_ctrl_ops
240 };
241