xref: /freebsd/sys/dev/aq/aq_fw.c (revision 493d26c58e732dcfcdd87993ef71880adfe9d0cb)
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 <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 // Timeouts
66 #define RBL_TIMEOUT_MS              10000
67 #define MAC_FW_START_TIMEOUT_MS     10000
68 #define FW_LOADER_START_TIMEOUT_MS  10000
69 
70 const u32 NO_RESET_SCRATCHPAD_ADDRESS = 0;
71 const u32 NO_RESET_SCRATCHPAD_LEN_RES = 1;
72 const u32 NO_RESET_SCRATCHPAD_RBL_STATUS = 2;
73 const u32 NO_RESET_SCRATCHPAD_RBL_STATUS_2 = 3;
74 const u32 WRITE_DATA_COMPLETE = 0x55555555;
75 const u32 WRITE_DATA_CHUNK_DONE = 0xaaaaaaaa;
76 const u32 WRITE_DATA_FAIL_WRONG_ADDRESS = 0x66666666;
77 
78 const u32 WAIT_WRITE_TIMEOUT = 1;
79 const u32 WAIT_WRITE_TIMEOUT_COUNT = 1000;
80 
81 const u32 RBL_STATUS_SUCCESS = 0xabba;
82 const u32 RBL_STATUS_FAILURE = 0xbad;
83 const u32 RBL_STATUS_HOST_BOOT = 0xf1a7;
84 
85 const u32 SCRATCHPAD_FW_LOADER_STATUS = (0x40 / sizeof(u32));
86 
87 
88 extern struct aq_firmware_ops aq_fw1x_ops;
89 extern struct aq_firmware_ops aq_fw2x_ops;
90 
91 
92 int mac_soft_reset_(struct aq_hw* hw, aq_fw_bootloader_mode* mode);
93 int mac_soft_reset_flb_(struct aq_hw* hw);
94 int mac_soft_reset_rbl_(struct aq_hw* hw, aq_fw_bootloader_mode* mode);
95 int wait_init_mac_firmware_(struct aq_hw* hw);
96 
97 
aq_fw_reset(struct aq_hw * hw)98 int aq_fw_reset(struct aq_hw* hw)
99 {
100     int ver = AQ_READ_REG(hw, 0x18);
101     u32 bootExitCode = 0;
102     int k;
103 
104     for (k = 0; k < 1000; ++k) {
105         u32 flbStatus = reg_glb_daisy_chain_status1_get(hw);
106         bootExitCode = AQ_READ_REG(hw, 0x388);
107         if (flbStatus != 0x06000000 || bootExitCode != 0)
108             break;
109     }
110 
111     if (k == 1000) {
112         aq_log_error("Neither RBL nor FLB started");
113         return (-EBUSY);
114     }
115 
116     hw->rbl_enabled = bootExitCode != 0;
117 
118     trace(dbg_init, "RBL enabled = %d", hw->rbl_enabled);
119 
120     /* Having FW version 0 is an indicator that cold start
121      * is in progress. This means two things:
122      * 1) Driver have to wait for FW/HW to finish boot (500ms giveup)
123      * 2) Driver may skip reset sequence and save time.
124      */
125     if (hw->fast_start_enabled && !ver) {
126         int err = wait_init_mac_firmware_(hw);
127         /* Skip reset as it just completed */
128         if (!err)
129             return (0);
130     }
131 
132     aq_fw_bootloader_mode mode = boot_mode_unknown;
133     int err = mac_soft_reset_(hw, &mode);
134     if (err < 0) {
135         aq_log_error("MAC reset failed: %d", err);
136         return (err);
137     }
138 
139     switch (mode) {
140     case boot_mode_flb:
141         aq_log("FLB> F/W successfully loaded from flash.");
142         hw->flash_present = true;
143         return wait_init_mac_firmware_(hw);
144 
145     case boot_mode_rbl_flash:
146         aq_log("RBL> F/W loaded from flash. Host Bootload disabled.");
147         hw->flash_present = true;
148         return wait_init_mac_firmware_(hw);
149 
150     case boot_mode_unknown:
151         aq_log_error("F/W bootload error: unknown bootloader type");
152         return (-ENOTSUP);
153 
154     case boot_mode_rbl_host_bootload:
155 #if AQ_CFG_HOST_BOOT_DISABLE
156         aq_log_error("RBL> Host Bootload mode: this driver does not support Host Boot");
157         return (-ENOTSUP);
158 #else
159         trace(dbg_init, "RBL> Host Bootload mode");
160         break;
161 #endif // HOST_BOOT_DISABLE
162     }
163 
164     /*
165      * #todo: Host Boot
166      */
167     aq_log_error("RBL> F/W Host Bootload not implemented");
168 
169     return (-ENOTSUP);
170 }
171 
aq_fw_ops_init(struct aq_hw * hw)172 int aq_fw_ops_init(struct aq_hw* hw)
173 {
174     if (hw->fw_version.raw == 0)
175         hw->fw_version.raw = AQ_READ_REG(hw, 0x18);
176 
177     aq_log("MAC F/W version is %d.%d.%d",
178         hw->fw_version.major_version, hw->fw_version.minor_version,
179         hw->fw_version.build_number);
180 
181     if (hw->fw_version.major_version == 1) {
182         trace(dbg_init, "using F/W ops v1.x");
183         hw->fw_ops = &aq_fw1x_ops;
184         return (EOK);
185     } else if (hw->fw_version.major_version >= 2) {
186         trace(dbg_init, "using F/W ops v2.x");
187         hw->fw_ops = &aq_fw2x_ops;
188         return (EOK);
189     }
190 
191     aq_log_error("aq_fw_ops_init(): invalid F/W version %#x", hw->fw_version.raw);
192     return (-ENOTSUP);
193 }
194 
195 
mac_soft_reset_(struct aq_hw * hw,aq_fw_bootloader_mode * mode)196 int mac_soft_reset_(struct aq_hw* hw, aq_fw_bootloader_mode* mode /*= nullptr*/)
197 {
198     if (hw->rbl_enabled) {
199         return mac_soft_reset_rbl_(hw, mode);
200     } else {
201         if (mode)
202             *mode = boot_mode_flb;
203 
204         return mac_soft_reset_flb_(hw);
205     }
206 }
207 
mac_soft_reset_flb_(struct aq_hw * hw)208 int mac_soft_reset_flb_(struct aq_hw* hw)
209 {
210     int k;
211 
212     reg_global_ctl2_set(hw, 0x40e1);
213     // Let Felicity hardware to complete SMBUS transaction before Global software reset.
214     msec_delay(50);
215 
216     /*
217      * If SPI burst transaction was interrupted(before running the script), global software
218      * reset may not clear SPI interface. Clean it up manually before global reset.
219      */
220     reg_glb_nvr_provisioning2_set(hw, 0xa0);
221     reg_glb_nvr_interface1_set(hw, 0x9f);
222     reg_glb_nvr_interface1_set(hw, 0x809f);
223     msec_delay(50);
224 
225     reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
226 
227     // Kickstart.
228     reg_global_ctl2_set(hw, 0x80e0);
229     reg_mif_power_gating_enable_control_set(hw, 0);
230     if (!hw->fast_start_enabled)
231         reg_glb_general_provisioning9_set(hw, 1);
232 
233     /*
234      * For the case SPI burst transaction was interrupted (by MCP reset above),
235      * wait until it is completed by hardware.
236      */
237     msec_delay(50); // Sleep for 10 ms.
238 
239     /* MAC Kickstart */
240     if (!hw->fast_start_enabled) {
241         reg_global_ctl2_set(hw, 0x180e0);
242 
243         u32 flb_status = 0;
244         int k;
245         for (k = 0; k < 1000; ++k) {
246             flb_status = reg_glb_daisy_chain_status1_get(hw) & 0x10;
247             if (flb_status != 0)
248                 break;
249             msec_delay(10); // Sleep for 10 ms.
250         }
251 
252         if (flb_status == 0) {
253             trace_error(dbg_init, "FLB> MAC kickstart failed: timed out");
254             return (false);
255         }
256 
257         trace(dbg_init, "FLB> MAC kickstart done, %d ms", k);
258         /* FW reset */
259         reg_global_ctl2_set(hw, 0x80e0);
260         // Let Felicity hardware complete SMBUS transaction before Global software reset.
261         msec_delay(50);
262     }
263     reg_glb_cpu_sem_set(hw, 1, 0);
264 
265     // PHY Kickstart: #undone
266 
267     // Global software reset
268     rx_rx_reg_res_dis_set(hw, 0);
269     tx_tx_reg_res_dis_set(hw, 0);
270     mpi_tx_reg_res_dis_set(hw, 0);
271     reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
272 
273     bool restart_completed = false;
274     for (k = 0; k < 1000; ++k) {
275         restart_completed = reg_glb_fw_image_id1_get(hw) != 0;
276         if (restart_completed)
277             break;
278         msec_delay(10);
279     }
280 
281     if (!restart_completed) {
282         trace_error(dbg_init, "FLB> Global Soft Reset failed");
283         return (false);
284     }
285 
286     trace(dbg_init, "FLB> F/W restart: %d ms", k * 10);
287     return (true);
288 }
289 
mac_soft_reset_rbl_(struct aq_hw * hw,aq_fw_bootloader_mode * mode)290 int mac_soft_reset_rbl_(struct aq_hw* hw, aq_fw_bootloader_mode* mode)
291 {
292     trace(dbg_init, "RBL> MAC reset STARTED!");
293 
294     reg_global_ctl2_set(hw, 0x40e1);
295     reg_glb_cpu_sem_set(hw, 1, 0);
296     reg_mif_power_gating_enable_control_set(hw, 0);
297 
298     // MAC FW will reload PHY FW if 1E.1000.3 was cleaned - #undone
299 
300     reg_glb_cpu_no_reset_scratchpad_set(hw, 0xDEAD, NO_RESET_SCRATCHPAD_RBL_STATUS);
301 
302     // Global software reset
303     rx_rx_reg_res_dis_set(hw, 0);
304     tx_tx_reg_res_dis_set(hw, 0);
305     mpi_tx_reg_res_dis_set(hw, 0);
306     reg_glb_standard_ctl1_set(hw, (reg_glb_standard_ctl1_get(hw) & ~glb_reg_res_dis_msk) | glb_soft_res_msk);
307 
308     reg_global_ctl2_set(hw, 0x40e0);
309 
310     // Wait for RBL to finish boot process.
311     u16 rbl_status = 0;
312     for (int k = 0; k < RBL_TIMEOUT_MS; ++k) {
313         rbl_status = LOWORD(reg_glb_cpu_no_reset_scratchpad_get(hw, NO_RESET_SCRATCHPAD_RBL_STATUS));
314         if (rbl_status != 0 && rbl_status != 0xDEAD)
315             break;
316 
317         msec_delay(1);
318     }
319 
320     if (rbl_status == 0 || rbl_status == 0xDEAD) {
321         trace_error(dbg_init, "RBL> RBL restart failed: timeout");
322         return (-EBUSY);
323     }
324 
325     if (rbl_status == RBL_STATUS_SUCCESS) {
326         if (mode)
327             *mode = boot_mode_rbl_flash;
328         trace(dbg_init, "RBL> reset complete! [Flash]");
329     } else if (rbl_status == RBL_STATUS_HOST_BOOT) {
330         if (mode)
331             *mode = boot_mode_rbl_host_bootload;
332         trace(dbg_init, "RBL> reset complete! [Host Bootload]");
333     } else {
334         trace_error(dbg_init, "unknown RBL status 0x%x", rbl_status);
335         return (-EBUSY);
336     }
337 
338     return (EOK);
339 }
340 
wait_init_mac_firmware_(struct aq_hw * hw)341 int wait_init_mac_firmware_(struct aq_hw* hw)
342 {
343     for (int i = 0; i < MAC_FW_START_TIMEOUT_MS; ++i) {
344         if ((hw->fw_version.raw = AQ_READ_REG(hw, 0x18)) != 0)
345             return (EOK);
346 
347         msec_delay(1);
348     }
349 
350     trace_error(dbg_init, "timeout waiting for reg 0x18. MAC f/w NOT READY");
351     return (-EBUSY);
352 }
353