xref: /freebsd/sys/dev/aq/aq_fw.c (revision 668423f75b4d9006f16847b415c861defb8267d7)
1 /*
2  * aQuantia Corporation Network Driver
3  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *   (1) Redistributions of source code must retain the above
10  *   copyright notice, this list of conditions and the following
11  *   disclaimer.
12  *
13  *   (2) Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following
15  *   disclaimer in the documentation and/or other materials provided
16  *   with the distribution.
17  *
18  *   (3) The name of the author may not be used to endorse or promote
19  *   products derived from this software without specific prior
20  *   written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * @file aq_fw.c
35  * Firmware-related functions implementation.
36  * @date 2017.12.07  @author roman.agafonov@aquantia.com
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 #include <sys/errno.h>
43 
44 #include "aq_common.h"
45 
46 #include "aq_hw.h"
47 #include "aq_hw_llh.h"
48 #include "aq_hw_llh_internal.h"
49 
50 #include "aq_fw.h"
51 #include "aq_common.h"
52 
53 #include "aq_dbg.h"
54 
55 
56 typedef enum aq_fw_bootloader_mode
57 {
58 	boot_mode_unknown = 0,
59 	boot_mode_flb,
60 	boot_mode_rbl_flash,
61 	boot_mode_rbl_host_bootload,
62 } aq_fw_bootloader_mode;
63 
64 #define AQ_CFG_HOST_BOOT_DISABLE 0
65 
66 // Timeouts
67 #define RBL_TIMEOUT_MS              10000
68 #define MAC_FW_START_TIMEOUT_MS     10000
69 #define FW_LOADER_START_TIMEOUT_MS  10000
70 
71 const uint32_t NO_RESET_SCRATCHPAD_ADDRESS = 0;
72 const uint32_t NO_RESET_SCRATCHPAD_LEN_RES = 1;
73 const uint32_t NO_RESET_SCRATCHPAD_RBL_STATUS = 2;
74 const uint32_t NO_RESET_SCRATCHPAD_RBL_STATUS_2 = 3;
75 const uint32_t WRITE_DATA_COMPLETE = 0x55555555;
76 const uint32_t WRITE_DATA_CHUNK_DONE = 0xaaaaaaaa;
77 const uint32_t WRITE_DATA_FAIL_WRONG_ADDRESS = 0x66666666;
78 
79 const uint32_t WAIT_WRITE_TIMEOUT = 1;
80 const uint32_t WAIT_WRITE_TIMEOUT_COUNT = 1000;
81 
82 const uint32_t RBL_STATUS_SUCCESS = 0xabba;
83 const uint32_t RBL_STATUS_FAILURE = 0xbad;
84 const uint32_t RBL_STATUS_HOST_BOOT = 0xf1a7;
85 
86 const uint32_t SCRATCHPAD_FW_LOADER_STATUS = (0x40 / sizeof(uint32_t));
87 
88 
89 extern struct aq_firmware_ops aq_fw1x_ops;
90 extern struct aq_firmware_ops aq_fw2x_ops;
91 
92 
93 int mac_soft_reset_(struct aq_hw* hw, aq_fw_bootloader_mode* mode);
94 int mac_soft_reset_flb_(struct aq_hw* hw);
95 int mac_soft_reset_rbl_(struct aq_hw* hw, aq_fw_bootloader_mode* mode);
96 int wait_init_mac_firmware_(struct aq_hw* hw);
97 
98 
99 int
aq_fw_reset(struct aq_hw * hw)100 aq_fw_reset(struct aq_hw* hw)
101 {
102 	int ver = AQ_READ_REG(hw, 0x18);
103 	uint32_t bootExitCode = 0;
104 	int k;
105 
106 	for (k = 0; k < 1000; ++k) {
107 		uint32_t flbStatus = reg_glb_daisy_chain_status1_get(hw);
108 		bootExitCode = AQ_READ_REG(hw, 0x388);
109 		if (flbStatus != 0x06000000 || bootExitCode != 0)
110 			break;
111 	}
112 
113 	if (k == 1000) {
114 		aq_log_error("Neither RBL nor FLB started");
115 		return (-EBUSY);
116 	}
117 
118 	hw->rbl_enabled = bootExitCode != 0;
119 
120 	trace(dbg_init, "RBL enabled = %d", hw->rbl_enabled);
121 
122 	/* Having FW version 0 is an indicator that cold start
123 	 * is in progress. This means two things:
124 	 * 1) Driver have to wait for FW/HW to finish boot (500ms giveup)
125 	 * 2) Driver may skip reset sequence and save time.
126 	 */
127 	if (hw->fast_start_enabled && !ver) {
128 		int err = wait_init_mac_firmware_(hw);
129 		/* Skip reset as it just completed */
130 		if (!err)
131 			return (0);
132 	}
133 
134 	aq_fw_bootloader_mode mode = boot_mode_unknown;
135 	int err = mac_soft_reset_(hw, &mode);
136 	if (err < 0) {
137 		aq_log_error("MAC reset failed: %d", err);
138 		return (err);
139 	}
140 
141 	switch (mode) {
142 	case boot_mode_flb:
143 		aq_log("FLB> F/W successfully loaded from flash.");
144 		hw->flash_present = true;
145 		return wait_init_mac_firmware_(hw);
146 
147 	case boot_mode_rbl_flash:
148 		aq_log("RBL> F/W loaded from flash. Host Bootload disabled.");
149 		hw->flash_present = true;
150 		return wait_init_mac_firmware_(hw);
151 
152 	case boot_mode_unknown:
153 		aq_log_error("F/W bootload error: unknown bootloader type");
154 		return (-ENOTSUP);
155 
156 	case boot_mode_rbl_host_bootload:
157 #if AQ_CFG_HOST_BOOT_DISABLE
158 		aq_log_error("RBL> Host Bootload mode: this driver does not support Host Boot");
159 		return (-ENOTSUP);
160 #else
161 		trace(dbg_init, "RBL> Host Bootload mode");
162 		break;
163 #endif // HOST_BOOT_DISABLE
164 	}
165 
166 	/*
167 	 * #todo: Host Boot
168 	 */
169 	aq_log_error("RBL> F/W Host Bootload not implemented");
170 
171 	return (-ENOTSUP);
172 }
173 
174 int
aq_fw_ops_init(struct aq_hw * hw)175 aq_fw_ops_init(struct aq_hw* hw)
176 {
177 	if (hw->fw_version.raw == 0)
178 		hw->fw_version.raw = AQ_READ_REG(hw, 0x18);
179 
180 	aq_log("MAC F/W version is %d.%d.%d",
181 	    hw->fw_version.major_version, hw->fw_version.minor_version,
182 	    hw->fw_version.build_number);
183 
184 	if (hw->fw_version.major_version == 1) {
185 		trace(dbg_init, "using F/W ops v1.x");
186 		hw->fw_ops = &aq_fw1x_ops;
187 		return (EOK);
188 	} else if (hw->fw_version.major_version >= 2) {
189 		trace(dbg_init, "using F/W ops v2.x");
190 		hw->fw_ops = &aq_fw2x_ops;
191 		return (EOK);
192 	}
193 
194 	aq_log_error("aq_fw_ops_init(): invalid F/W version %#x",
195 	    hw->fw_version.raw);
196 	return (-ENOTSUP);
197 }
198 
199 
200 int
mac_soft_reset_(struct aq_hw * hw,aq_fw_bootloader_mode * mode)201 mac_soft_reset_(struct aq_hw* hw, aq_fw_bootloader_mode* mode /*= nullptr*/)
202 {
203 	if (hw->rbl_enabled) {
204 		return mac_soft_reset_rbl_(hw, mode);
205 	} else {
206 		if (mode)
207 			*mode = boot_mode_flb;
208 
209 		return mac_soft_reset_flb_(hw);
210 	}
211 }
212 
213 int
mac_soft_reset_flb_(struct aq_hw * hw)214 mac_soft_reset_flb_(struct aq_hw* hw)
215 {
216 	int k;
217 
218 	reg_global_ctl2_set(hw, 0x40e1);
219 	// Let Felicity hardware complete SMBUS transaction before Global
220 	// software reset.
221 	msec_delay(50);
222 
223 	/*
224 	 * If SPI burst transaction was interrupted(before running the script),
225 	 * global software reset may not clear SPI interface. Clean it up
226 	 * manually before global reset.
227 	 */
228 	reg_glb_nvr_provisioning2_set(hw, 0xa0);
229 	reg_glb_nvr_interface1_set(hw, 0x9f);
230 	reg_glb_nvr_interface1_set(hw, 0x809f);
231 	msec_delay(50);
232 
233 	reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
234 
235 	// Kickstart.
236 	reg_global_ctl2_set(hw, 0x80e0);
237 	reg_mif_power_gating_enable_control_set(hw, 0);
238 	if (!hw->fast_start_enabled)
239 		reg_glb_general_provisioning9_set(hw, 1);
240 
241 	/*
242 	 * For the case SPI burst transaction was interrupted (by MCP reset
243 	 * above), wait until it is completed by hardware.
244 	 */
245 	msec_delay(50); // Sleep for 10 ms.
246 
247 	/* MAC Kickstart */
248 	if (!hw->fast_start_enabled) {
249 		reg_global_ctl2_set(hw, 0x180e0);
250 
251 		uint32_t flb_status = 0;
252 		int k;
253 		for (k = 0; k < 1000; ++k) {
254 			flb_status = reg_glb_daisy_chain_status1_get(hw) & 0x10;
255 			if (flb_status != 0)
256 				break;
257 			msec_delay(10); // Sleep for 10 ms.
258 		}
259 
260 		if (flb_status == 0) {
261 			trace_error(dbg_init,
262 			    "FLB> MAC kickstart failed: timed out");
263 			return (false);
264 		}
265 
266 		trace(dbg_init, "FLB> MAC kickstart done, %d ms", k);
267 		/* FW reset */
268 		reg_global_ctl2_set(hw, 0x80e0);
269 		// Let Felicity hardware complete SMBUS transaction before
270 		// Global software reset.
271 		msec_delay(50);
272 	}
273 	reg_glb_cpu_sem_set(hw, 1, 0);
274 
275 	// PHY Kickstart: #undone
276 
277 	// Global software reset
278 	rx_rx_reg_res_dis_set(hw, 0);
279 	tx_tx_reg_res_dis_set(hw, 0);
280 	mpi_tx_reg_res_dis_set(hw, 0);
281 	reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
282 
283 	bool restart_completed = false;
284 	for (k = 0; k < 1000; ++k) {
285 		restart_completed = reg_glb_fw_image_id1_get(hw) != 0;
286 		if (restart_completed)
287 			break;
288 		msec_delay(10);
289 	}
290 
291 	if (!restart_completed) {
292 		trace_error(dbg_init, "FLB> Global Soft Reset failed");
293 		return (false);
294 	}
295 
296 	trace(dbg_init, "FLB> F/W restart: %d ms", k * 10);
297 	return (true);
298 }
299 
300 int
mac_soft_reset_rbl_(struct aq_hw * hw,aq_fw_bootloader_mode * mode)301 mac_soft_reset_rbl_(struct aq_hw* hw, aq_fw_bootloader_mode* mode)
302 {
303 	trace(dbg_init, "RBL> MAC reset STARTED!");
304 
305 	reg_global_ctl2_set(hw, 0x40e1);
306 	reg_glb_cpu_sem_set(hw, 1, 0);
307 	reg_mif_power_gating_enable_control_set(hw, 0);
308 
309 	// MAC FW will reload PHY FW if 1E.1000.3 was cleaned - #undone
310 
311 	reg_glb_cpu_no_reset_scratchpad_set(hw, 0xDEAD,
312 	    NO_RESET_SCRATCHPAD_RBL_STATUS);
313 
314 	// Global software reset
315 	rx_rx_reg_res_dis_set(hw, 0);
316 	tx_tx_reg_res_dis_set(hw, 0);
317 	mpi_tx_reg_res_dis_set(hw, 0);
318 	reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
319 
320 	reg_global_ctl2_set(hw, 0x40e0);
321 
322 	// Wait for RBL to finish boot process.
323 	uint16_t rbl_status = 0;
324 	for (int k = 0; k < RBL_TIMEOUT_MS; ++k) {
325 		rbl_status = LOWORD(reg_glb_cpu_no_reset_scratchpad_get(hw, NO_RESET_SCRATCHPAD_RBL_STATUS));
326 		if (rbl_status != 0 && rbl_status != 0xDEAD)
327 			break;
328 
329 		msec_delay(1);
330 	}
331 
332 	if (rbl_status == 0 || rbl_status == 0xDEAD) {
333 		trace_error(dbg_init, "RBL> RBL restart failed: timeout");
334 		return (-EBUSY);
335 	}
336 
337 	if (rbl_status == RBL_STATUS_SUCCESS) {
338 		if (mode)
339 			*mode = boot_mode_rbl_flash;
340 		trace(dbg_init, "RBL> reset complete! [Flash]");
341 	} else if (rbl_status == RBL_STATUS_HOST_BOOT) {
342 		if (mode)
343 			*mode = boot_mode_rbl_host_bootload;
344 		trace(dbg_init, "RBL> reset complete! [Host Bootload]");
345 	} else {
346 		trace_error(dbg_init, "unknown RBL status 0x%x", rbl_status);
347 		return (-EBUSY);
348 	}
349 
350 	return (EOK);
351 }
352 
353 int
wait_init_mac_firmware_(struct aq_hw * hw)354 wait_init_mac_firmware_(struct aq_hw* hw)
355 {
356 	for (int i = 0; i < MAC_FW_START_TIMEOUT_MS; ++i) {
357 		if ((hw->fw_version.raw = AQ_READ_REG(hw, 0x18)) != 0)
358 			return (EOK);
359 
360 		msec_delay(1);
361 	}
362 
363 	trace_error(dbg_init,
364 	    "timeout waiting for reg 0x18. MAC f/w NOT READY");
365 	return (-EBUSY);
366 }
367