xref: /freebsd/sys/dev/aq/aq_fw1x.c (revision 493d26c58e732dcfcdd87993ef71880adfe9d0cb)
1*493d26c5SEd Maste /**
2*493d26c5SEd Maste  * aQuantia Corporation Network Driver
3*493d26c5SEd Maste  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
4*493d26c5SEd Maste  *
5*493d26c5SEd Maste  * Redistribution and use in source and binary forms, with or without
6*493d26c5SEd Maste  * modification, are permitted provided that the following conditions
7*493d26c5SEd Maste  * are met:
8*493d26c5SEd Maste  *
9*493d26c5SEd Maste  *   (1) Redistributions of source code must retain the above
10*493d26c5SEd Maste  *   copyright notice, this list of conditions and the following
11*493d26c5SEd Maste  *   disclaimer.
12*493d26c5SEd Maste  *
13*493d26c5SEd Maste  *   (2) Redistributions in binary form must reproduce the above
14*493d26c5SEd Maste  *   copyright notice, this list of conditions and the following
15*493d26c5SEd Maste  *   disclaimer in the documentation and/or other materials provided
16*493d26c5SEd Maste  *   with the distribution.
17*493d26c5SEd Maste  *
18*493d26c5SEd Maste  *   (3) The name of the author may not be used to endorse or promote
19*493d26c5SEd Maste  *   products derived from this software without specific prior
20*493d26c5SEd Maste  *   written permission.
21*493d26c5SEd Maste  *
22*493d26c5SEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23*493d26c5SEd Maste  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24*493d26c5SEd Maste  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*493d26c5SEd Maste  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
26*493d26c5SEd Maste  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*493d26c5SEd Maste  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28*493d26c5SEd Maste  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29*493d26c5SEd Maste  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30*493d26c5SEd Maste  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31*493d26c5SEd Maste  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32*493d26c5SEd Maste  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33*493d26c5SEd Maste  */
34*493d26c5SEd Maste 
35*493d26c5SEd Maste #include <sys/cdefs.h>
36*493d26c5SEd Maste __FBSDID("$FreeBSD$");
37*493d26c5SEd Maste 
38*493d26c5SEd Maste #include <errno.h>
39*493d26c5SEd Maste 
40*493d26c5SEd Maste #include "aq_common.h"
41*493d26c5SEd Maste #include "aq_hw.h"
42*493d26c5SEd Maste #include "aq_hw_llh.h"
43*493d26c5SEd Maste #include "aq_hw_llh_internal.h"
44*493d26c5SEd Maste #include "aq_fw.h"
45*493d26c5SEd Maste #include "aq_dbg.h"
46*493d26c5SEd Maste 
47*493d26c5SEd Maste 
48*493d26c5SEd Maste #define FW1X_MPI_CONTROL_ADR    0x368
49*493d26c5SEd Maste #define FW1X_MPI_STATE_ADR      0x36C
50*493d26c5SEd Maste 
51*493d26c5SEd Maste 
52*493d26c5SEd Maste typedef enum fw1x_mode {
53*493d26c5SEd Maste     FW1X_MPI_DEINIT = 0,
54*493d26c5SEd Maste     FW1X_MPI_RESERVED = 1,
55*493d26c5SEd Maste     FW1X_MPI_INIT = 2,
56*493d26c5SEd Maste     FW1X_MPI_POWER = 4,
57*493d26c5SEd Maste } fw1x_mode;
58*493d26c5SEd Maste 
59*493d26c5SEd Maste typedef enum aq_fw1x_rate {
60*493d26c5SEd Maste     FW1X_RATE_10G   = 1 << 0,
61*493d26c5SEd Maste     FW1X_RATE_5G    = 1 << 1,
62*493d26c5SEd Maste     FW1X_RATE_5GSR  = 1 << 2,
63*493d26c5SEd Maste     FW1X_RATE_2G5   = 1 << 3,
64*493d26c5SEd Maste     FW1X_RATE_1G    = 1 << 4,
65*493d26c5SEd Maste     FW1X_RATE_100M  = 1 << 5,
66*493d26c5SEd Maste     FW1X_RATE_INVALID = 1 << 6,
67*493d26c5SEd Maste } aq_fw1x_rate;
68*493d26c5SEd Maste 
69*493d26c5SEd Maste typedef union fw1x_state_reg {
70*493d26c5SEd Maste     u32 val;
71*493d26c5SEd Maste     struct {
72*493d26c5SEd Maste         u8 mode;
73*493d26c5SEd Maste         u8 reserved1;
74*493d26c5SEd Maste         u8 speed;
75*493d26c5SEd Maste         u8 reserved2 : 1;
76*493d26c5SEd Maste         u8 disableDirtyWake : 1;
77*493d26c5SEd Maste         u8 reserved3 : 2;
78*493d26c5SEd Maste         u8 downshift : 4;
79*493d26c5SEd Maste     };
80*493d26c5SEd Maste } fw1x_state_reg;
81*493d26c5SEd Maste 
82*493d26c5SEd Maste int fw1x_reset(struct aq_hw* hw);
83*493d26c5SEd Maste 
84*493d26c5SEd Maste int fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state_e mode, aq_fw_link_speed_t speed);
85*493d26c5SEd Maste int fw1x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state_e* mode, aq_fw_link_speed_t* speed, aq_fw_link_fc_t* fc);
86*493d26c5SEd Maste int fw1x_get_mac_addr(struct aq_hw* hw, u8* mac_addr);
87*493d26c5SEd Maste int fw1x_get_stats(struct aq_hw* hw, struct aq_hw_stats_s* stats);
88*493d26c5SEd Maste 
89*493d26c5SEd Maste 
90*493d26c5SEd Maste static
mpi_mode_to_fw1x_(enum aq_hw_fw_mpi_state_e mode)91*493d26c5SEd Maste fw1x_mode mpi_mode_to_fw1x_(enum aq_hw_fw_mpi_state_e mode)
92*493d26c5SEd Maste {
93*493d26c5SEd Maste     switch (mode) {
94*493d26c5SEd Maste     case MPI_DEINIT:
95*493d26c5SEd Maste         return (FW1X_MPI_DEINIT);
96*493d26c5SEd Maste 
97*493d26c5SEd Maste     case MPI_INIT:
98*493d26c5SEd Maste         return (FW1X_MPI_INIT);
99*493d26c5SEd Maste 
100*493d26c5SEd Maste     case MPI_POWER:
101*493d26c5SEd Maste         return (FW1X_MPI_POWER);
102*493d26c5SEd Maste 
103*493d26c5SEd Maste     case MPI_RESET:
104*493d26c5SEd Maste         return (FW1X_MPI_RESERVED);
105*493d26c5SEd Maste     }
106*493d26c5SEd Maste 
107*493d26c5SEd Maste     /*
108*493d26c5SEd Maste      * We shouldn't get here.
109*493d26c5SEd Maste      */
110*493d26c5SEd Maste 
111*493d26c5SEd Maste     return (FW1X_MPI_RESERVED);
112*493d26c5SEd Maste }
113*493d26c5SEd Maste 
114*493d26c5SEd Maste static
link_speed_mask_to_fw1x_(u32 speed)115*493d26c5SEd Maste aq_fw1x_rate link_speed_mask_to_fw1x_(u32 /*aq_fw_link_speed*/ speed)
116*493d26c5SEd Maste {
117*493d26c5SEd Maste     u32 rate = 0;
118*493d26c5SEd Maste     if (speed & aq_fw_10G)
119*493d26c5SEd Maste         rate |= FW1X_RATE_10G;
120*493d26c5SEd Maste 
121*493d26c5SEd Maste     if (speed & aq_fw_5G) {
122*493d26c5SEd Maste         rate |= FW1X_RATE_5G;
123*493d26c5SEd Maste         rate |= FW1X_RATE_5GSR;
124*493d26c5SEd Maste     }
125*493d26c5SEd Maste 
126*493d26c5SEd Maste     if (speed & aq_fw_2G5)
127*493d26c5SEd Maste         rate |= FW1X_RATE_2G5;
128*493d26c5SEd Maste 
129*493d26c5SEd Maste     if (speed & aq_fw_1G)
130*493d26c5SEd Maste         rate |= FW1X_RATE_1G;
131*493d26c5SEd Maste 
132*493d26c5SEd Maste     if (speed & aq_fw_100M)
133*493d26c5SEd Maste         rate |= FW1X_RATE_100M;
134*493d26c5SEd Maste 
135*493d26c5SEd Maste     return ((aq_fw1x_rate)rate);
136*493d26c5SEd Maste }
137*493d26c5SEd Maste 
138*493d26c5SEd Maste static
fw1x_rate_to_link_speed_(aq_fw1x_rate rate)139*493d26c5SEd Maste aq_fw_link_speed_t fw1x_rate_to_link_speed_(aq_fw1x_rate rate)
140*493d26c5SEd Maste {
141*493d26c5SEd Maste     switch (rate) {
142*493d26c5SEd Maste     case FW1X_RATE_10G:
143*493d26c5SEd Maste         return (aq_fw_10G);
144*493d26c5SEd Maste     case FW1X_RATE_5G:
145*493d26c5SEd Maste     case FW1X_RATE_5GSR:
146*493d26c5SEd Maste         return (aq_fw_5G);
147*493d26c5SEd Maste     case FW1X_RATE_2G5:
148*493d26c5SEd Maste         return (aq_fw_2G5);
149*493d26c5SEd Maste     case FW1X_RATE_1G:
150*493d26c5SEd Maste         return (aq_fw_1G);
151*493d26c5SEd Maste     case FW1X_RATE_100M:
152*493d26c5SEd Maste         return (aq_fw_100M);
153*493d26c5SEd Maste     case FW1X_RATE_INVALID:
154*493d26c5SEd Maste         return (aq_fw_none);
155*493d26c5SEd Maste     }
156*493d26c5SEd Maste 
157*493d26c5SEd Maste     /*
158*493d26c5SEd Maste      * We should never get here.
159*493d26c5SEd Maste      */
160*493d26c5SEd Maste 
161*493d26c5SEd Maste     return (aq_fw_none);
162*493d26c5SEd Maste }
163*493d26c5SEd Maste 
fw1x_reset(struct aq_hw * hal)164*493d26c5SEd Maste int fw1x_reset(struct aq_hw* hal)
165*493d26c5SEd Maste {
166*493d26c5SEd Maste     u32 tid0 = ~0u; /*< Initial value of MBOX transactionId. */
167*493d26c5SEd Maste     struct aq_hw_fw_mbox mbox;
168*493d26c5SEd Maste     const int retryCount = 1000;
169*493d26c5SEd Maste 
170*493d26c5SEd Maste     for (int i = 0; i < retryCount; ++i) {
171*493d26c5SEd Maste         // Read the beginning of Statistics structure to capture the Transaction ID.
172*493d26c5SEd Maste         aq_hw_fw_downld_dwords(hal, hal->mbox_addr, (u32*)&mbox,
173*493d26c5SEd Maste             (u32)((char*)&mbox.stats - (char*)&mbox) / sizeof(u32));
174*493d26c5SEd Maste 
175*493d26c5SEd Maste         // Successfully read the stats.
176*493d26c5SEd Maste         if (tid0 == ~0U) {
177*493d26c5SEd Maste             // We have read the initial value.
178*493d26c5SEd Maste             tid0 = mbox.transaction_id;
179*493d26c5SEd Maste             continue;
180*493d26c5SEd Maste         } else if (mbox.transaction_id != tid0) {
181*493d26c5SEd Maste             /*
182*493d26c5SEd Maste              * Compare transaction ID to initial value.
183*493d26c5SEd Maste              * If it's different means f/w is alive. We're done.
184*493d26c5SEd Maste              */
185*493d26c5SEd Maste 
186*493d26c5SEd Maste             return (EOK);
187*493d26c5SEd Maste         }
188*493d26c5SEd Maste 
189*493d26c5SEd Maste         /*
190*493d26c5SEd Maste          * Transaction ID value haven't changed since last time.
191*493d26c5SEd Maste          * Try reading the stats again.
192*493d26c5SEd Maste          */
193*493d26c5SEd Maste         usec_delay(10);
194*493d26c5SEd Maste     }
195*493d26c5SEd Maste 
196*493d26c5SEd Maste     trace_error(dbg_init, "F/W 1.x reset finalize timeout");
197*493d26c5SEd Maste     return (-EBUSY);
198*493d26c5SEd Maste }
199*493d26c5SEd Maste 
fw1x_set_mode(struct aq_hw * hw,enum aq_hw_fw_mpi_state_e mode,aq_fw_link_speed_t speed)200*493d26c5SEd Maste int fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state_e mode, aq_fw_link_speed_t speed)
201*493d26c5SEd Maste {
202*493d26c5SEd Maste     union fw1x_state_reg state = {0};
203*493d26c5SEd Maste     state.mode = mpi_mode_to_fw1x_(mode);
204*493d26c5SEd Maste     state.speed = link_speed_mask_to_fw1x_(speed);
205*493d26c5SEd Maste 
206*493d26c5SEd Maste     trace(dbg_init, "fw1x> set mode %d, rate mask = %#x; raw = %#x", state.mode, state.speed, state.val);
207*493d26c5SEd Maste 
208*493d26c5SEd Maste     AQ_WRITE_REG(hw, FW1X_MPI_CONTROL_ADR, state.val);
209*493d26c5SEd Maste 
210*493d26c5SEd Maste     return (EOK);
211*493d26c5SEd Maste }
212*493d26c5SEd Maste 
fw1x_get_mode(struct aq_hw * hw,enum aq_hw_fw_mpi_state_e * mode,aq_fw_link_speed_t * speed,aq_fw_link_fc_t * fc)213*493d26c5SEd Maste int fw1x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state_e* mode, aq_fw_link_speed_t* speed, aq_fw_link_fc_t* fc)
214*493d26c5SEd Maste {
215*493d26c5SEd Maste     union fw1x_state_reg state = { .val = AQ_READ_REG(hw, AQ_HW_MPI_STATE_ADR) };
216*493d26c5SEd Maste 
217*493d26c5SEd Maste     trace(dbg_init, "fw1x> get_mode(): 0x36c -> %x, 0x368 -> %x", state.val, AQ_READ_REG(hw, AQ_HW_MPI_CONTROL_ADR));
218*493d26c5SEd Maste 
219*493d26c5SEd Maste     enum aq_hw_fw_mpi_state_e md = MPI_DEINIT;
220*493d26c5SEd Maste 
221*493d26c5SEd Maste     switch (state.mode) {
222*493d26c5SEd Maste     case FW1X_MPI_DEINIT:
223*493d26c5SEd Maste         md = MPI_DEINIT;
224*493d26c5SEd Maste         break;
225*493d26c5SEd Maste     case FW1X_MPI_RESERVED:
226*493d26c5SEd Maste         md = MPI_RESET;
227*493d26c5SEd Maste         break;
228*493d26c5SEd Maste     case FW1X_MPI_INIT:
229*493d26c5SEd Maste         md = MPI_INIT;
230*493d26c5SEd Maste         break;
231*493d26c5SEd Maste     case FW1X_MPI_POWER:
232*493d26c5SEd Maste         md = MPI_POWER;
233*493d26c5SEd Maste         break;
234*493d26c5SEd Maste     }
235*493d26c5SEd Maste 
236*493d26c5SEd Maste     if (mode)
237*493d26c5SEd Maste         *mode = md;
238*493d26c5SEd Maste 
239*493d26c5SEd Maste     if (speed)
240*493d26c5SEd Maste         *speed = fw1x_rate_to_link_speed_(state.speed);
241*493d26c5SEd Maste 
242*493d26c5SEd Maste     *fc = aq_fw_fc_none;
243*493d26c5SEd Maste 
244*493d26c5SEd Maste     AQ_DBG_EXIT(EOK);
245*493d26c5SEd Maste     return (EOK);
246*493d26c5SEd Maste }
247*493d26c5SEd Maste 
248*493d26c5SEd Maste 
fw1x_get_mac_addr(struct aq_hw * hw,u8 * mac)249*493d26c5SEd Maste int fw1x_get_mac_addr(struct aq_hw* hw, u8* mac)
250*493d26c5SEd Maste {
251*493d26c5SEd Maste     int err = -EFAULT;
252*493d26c5SEd Maste     u32 mac_addr[2];
253*493d26c5SEd Maste 
254*493d26c5SEd Maste     AQ_DBG_ENTER();
255*493d26c5SEd Maste 
256*493d26c5SEd Maste     u32 efuse_shadow_addr = AQ_READ_REG(hw, 0x374);
257*493d26c5SEd Maste     if (efuse_shadow_addr == 0) {
258*493d26c5SEd Maste         trace_error(dbg_init, "couldn't read eFUSE Shadow Address");
259*493d26c5SEd Maste         AQ_DBG_EXIT(-EFAULT);
260*493d26c5SEd Maste         return (-EFAULT);
261*493d26c5SEd Maste     }
262*493d26c5SEd Maste 
263*493d26c5SEd Maste     err = aq_hw_fw_downld_dwords(hw, efuse_shadow_addr + (40 * 4),
264*493d26c5SEd Maste         mac_addr, ARRAY_SIZE(mac_addr));
265*493d26c5SEd Maste     if (err < 0) {
266*493d26c5SEd Maste         mac_addr[0] = 0;
267*493d26c5SEd Maste         mac_addr[1] = 0;
268*493d26c5SEd Maste         AQ_DBG_EXIT(err);
269*493d26c5SEd Maste         return (err);
270*493d26c5SEd Maste     }
271*493d26c5SEd Maste 
272*493d26c5SEd Maste     mac_addr[0] = bswap32(mac_addr[0]);
273*493d26c5SEd Maste     mac_addr[1] = bswap32(mac_addr[1]);
274*493d26c5SEd Maste 
275*493d26c5SEd Maste     memcpy(mac, (u8*)mac_addr, ETH_MAC_LEN);
276*493d26c5SEd Maste 
277*493d26c5SEd Maste     trace(dbg_init, "fw1x> eFUSE MAC addr -> %02x-%02x-%02x-%02x-%02x-%02x",
278*493d26c5SEd Maste         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
279*493d26c5SEd Maste 
280*493d26c5SEd Maste     AQ_DBG_EXIT(EOK);
281*493d26c5SEd Maste     return (EOK);
282*493d26c5SEd Maste }
283*493d26c5SEd Maste 
fw1x_get_stats(struct aq_hw * hw,struct aq_hw_stats_s * stats)284*493d26c5SEd Maste int fw1x_get_stats(struct aq_hw* hw, struct aq_hw_stats_s* stats)
285*493d26c5SEd Maste {
286*493d26c5SEd Maste     int err = 0;
287*493d26c5SEd Maste 
288*493d26c5SEd Maste     AQ_DBG_ENTER();
289*493d26c5SEd Maste     err = aq_hw_fw_downld_dwords(hw, hw->mbox_addr, (u32*)(void*)&hw->mbox,
290*493d26c5SEd Maste         sizeof hw->mbox / sizeof(u32));
291*493d26c5SEd Maste 
292*493d26c5SEd Maste     if (err >= 0) {
293*493d26c5SEd Maste         if (stats != &hw->mbox.stats)
294*493d26c5SEd Maste             memcpy(stats, &hw->mbox.stats, sizeof *stats);
295*493d26c5SEd Maste 
296*493d26c5SEd Maste         stats->dpc = reg_rx_dma_stat_counter7get(hw);
297*493d26c5SEd Maste     }
298*493d26c5SEd Maste 
299*493d26c5SEd Maste     AQ_DBG_EXIT(err);
300*493d26c5SEd Maste     return (err);
301*493d26c5SEd Maste }
302*493d26c5SEd Maste 
303*493d26c5SEd Maste struct aq_firmware_ops aq_fw1x_ops =
304*493d26c5SEd Maste {
305*493d26c5SEd Maste     .reset = fw1x_reset,
306*493d26c5SEd Maste 
307*493d26c5SEd Maste     .set_mode = fw1x_set_mode,
308*493d26c5SEd Maste     .get_mode = fw1x_get_mode,
309*493d26c5SEd Maste 
310*493d26c5SEd Maste     .get_mac_addr = fw1x_get_mac_addr,
311*493d26c5SEd Maste     .get_stats = fw1x_get_stats,
312*493d26c5SEd Maste };
313*493d26c5SEd Maste 
314