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