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