Commit | Line | Data |
---|---|---|
8ceee660 BH |
1 | /**************************************************************************** |
2 | * Driver for Solarflare Solarstorm network controllers and boards | |
3 | * Copyright 2005-2006 Fen Systems Ltd. | |
4 | * Copyright 2006-2008 Solarflare Communications Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published | |
8 | * by the Free Software Foundation, incorporated herein by reference. | |
9 | */ | |
10 | ||
11 | #include <linux/delay.h> | |
12 | #include "net_driver.h" | |
13 | #include "efx.h" | |
14 | #include "falcon.h" | |
15 | #include "falcon_hwdefs.h" | |
16 | #include "falcon_io.h" | |
17 | #include "mac.h" | |
8ceee660 BH |
18 | #include "mdio_10g.h" |
19 | #include "phy.h" | |
20 | #include "boards.h" | |
21 | #include "workarounds.h" | |
22 | ||
8ceee660 BH |
23 | /************************************************************************** |
24 | * | |
25 | * MAC operations | |
26 | * | |
27 | *************************************************************************/ | |
8ceee660 BH |
28 | |
29 | /* Configure the XAUI driver that is an output from Falcon */ | |
30 | static void falcon_setup_xaui(struct efx_nic *efx) | |
31 | { | |
c1e5fcc9 | 32 | efx_oword_t sdctl, txdrv; |
8ceee660 BH |
33 | |
34 | /* Move the XAUI into low power, unless there is no PHY, in | |
35 | * which case the XAUI will have to drive a cable. */ | |
36 | if (efx->phy_type == PHY_TYPE_NONE) | |
37 | return; | |
38 | ||
c1e5fcc9 BH |
39 | falcon_read(efx, &sdctl, XX_SD_CTL_REG); |
40 | EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT); | |
41 | EFX_SET_OWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT); | |
42 | EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT); | |
43 | EFX_SET_OWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT); | |
44 | EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT); | |
45 | EFX_SET_OWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT); | |
46 | EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT); | |
47 | EFX_SET_OWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT); | |
48 | falcon_write(efx, &sdctl, XX_SD_CTL_REG); | |
49 | ||
50 | EFX_POPULATE_OWORD_8(txdrv, | |
8ceee660 BH |
51 | XX_DEQD, XX_TXDRV_DEQ_DEFAULT, |
52 | XX_DEQC, XX_TXDRV_DEQ_DEFAULT, | |
53 | XX_DEQB, XX_TXDRV_DEQ_DEFAULT, | |
54 | XX_DEQA, XX_TXDRV_DEQ_DEFAULT, | |
55 | XX_DTXD, XX_TXDRV_DTX_DEFAULT, | |
56 | XX_DTXC, XX_TXDRV_DTX_DEFAULT, | |
57 | XX_DTXB, XX_TXDRV_DTX_DEFAULT, | |
58 | XX_DTXA, XX_TXDRV_DTX_DEFAULT); | |
c1e5fcc9 | 59 | falcon_write(efx, &txdrv, XX_TXDRV_CTL_REG); |
8ceee660 BH |
60 | } |
61 | ||
ef08af03 | 62 | int falcon_reset_xaui(struct efx_nic *efx) |
8ceee660 | 63 | { |
c1e5fcc9 | 64 | efx_oword_t reg; |
8ceee660 BH |
65 | int count; |
66 | ||
67 | EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1); | |
c1e5fcc9 | 68 | falcon_write(efx, ®, XX_PWR_RST_REG); |
8ceee660 BH |
69 | |
70 | /* Give some time for the link to establish */ | |
71 | for (count = 0; count < 1000; count++) { /* wait upto 10ms */ | |
c1e5fcc9 BH |
72 | falcon_read(efx, ®, XX_PWR_RST_REG); |
73 | if (EFX_OWORD_FIELD(reg, XX_RST_XX_EN) == 0) { | |
8ceee660 BH |
74 | falcon_setup_xaui(efx); |
75 | return 0; | |
76 | } | |
77 | udelay(10); | |
78 | } | |
79 | EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n"); | |
80 | return -ETIMEDOUT; | |
81 | } | |
82 | ||
177dfcd8 | 83 | static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) |
8ceee660 | 84 | { |
c1e5fcc9 | 85 | efx_oword_t reg; |
8ceee660 | 86 | |
177dfcd8 BH |
87 | if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) |
88 | return; | |
8ceee660 | 89 | |
177dfcd8 BH |
90 | /* We expect xgmii faults if the wireside link is up */ |
91 | if (!EFX_WORKAROUND_5147(efx) || !efx->link_up) | |
92 | return; | |
8ceee660 | 93 | |
177dfcd8 BH |
94 | /* We can only use this interrupt to signal the negative edge of |
95 | * xaui_align [we have to poll the positive edge]. */ | |
96 | if (!efx->mac_up) | |
8ceee660 BH |
97 | return; |
98 | ||
99 | /* Flush the ISR */ | |
100 | if (enable) | |
c1e5fcc9 | 101 | falcon_read(efx, ®, XM_MGT_INT_REG_B0); |
8ceee660 | 102 | |
c1e5fcc9 | 103 | EFX_POPULATE_OWORD_2(reg, |
8ceee660 BH |
104 | XM_MSK_RMTFLT, !enable, |
105 | XM_MSK_LCLFLT, !enable); | |
c1e5fcc9 | 106 | falcon_write(efx, ®, XM_MGT_INT_MSK_REG_B0); |
8ceee660 BH |
107 | } |
108 | ||
177dfcd8 | 109 | /* Get status of XAUI link */ |
dc8cfa55 | 110 | bool falcon_xaui_link_ok(struct efx_nic *efx) |
8ceee660 | 111 | { |
c1e5fcc9 | 112 | efx_oword_t reg; |
dc8cfa55 BH |
113 | bool align_done, link_ok = false; |
114 | int sync_status; | |
8ceee660 | 115 | |
3273c2e8 | 116 | if (LOOPBACK_INTERNAL(efx)) |
dc8cfa55 | 117 | return true; |
3273c2e8 | 118 | |
8ceee660 | 119 | /* Read link status */ |
c1e5fcc9 | 120 | falcon_read(efx, ®, XX_CORE_STAT_REG); |
8ceee660 | 121 | |
c1e5fcc9 BH |
122 | align_done = EFX_OWORD_FIELD(reg, XX_ALIGN_DONE); |
123 | sync_status = EFX_OWORD_FIELD(reg, XX_SYNC_STAT); | |
8ceee660 | 124 | if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED)) |
dc8cfa55 | 125 | link_ok = true; |
8ceee660 BH |
126 | |
127 | /* Clear link status ready for next read */ | |
c1e5fcc9 BH |
128 | EFX_SET_OWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET); |
129 | EFX_SET_OWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET); | |
130 | EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET); | |
131 | falcon_write(efx, ®, XX_CORE_STAT_REG); | |
8ceee660 | 132 | |
177dfcd8 BH |
133 | /* If the link is up, then check the phy side of the xaui link */ |
134 | if (efx->link_up && link_ok) | |
dc8cfa55 | 135 | if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS)) |
8ceee660 | 136 | link_ok = mdio_clause45_phyxgxs_lane_sync(efx); |
8ceee660 BH |
137 | |
138 | return link_ok; | |
139 | } | |
140 | ||
141 | static void falcon_reconfigure_xmac_core(struct efx_nic *efx) | |
142 | { | |
143 | unsigned int max_frame_len; | |
c1e5fcc9 | 144 | efx_oword_t reg; |
04cc8cac | 145 | bool rx_fc = !!(efx->link_fc & EFX_FC_RX); |
8ceee660 BH |
146 | |
147 | /* Configure MAC - cut-thru mode is hard wired on */ | |
148 | EFX_POPULATE_DWORD_3(reg, | |
149 | XM_RX_JUMBO_MODE, 1, | |
150 | XM_TX_STAT_EN, 1, | |
151 | XM_RX_STAT_EN, 1); | |
c1e5fcc9 | 152 | falcon_write(efx, ®, XM_GLB_CFG_REG); |
8ceee660 BH |
153 | |
154 | /* Configure TX */ | |
155 | EFX_POPULATE_DWORD_6(reg, | |
156 | XM_TXEN, 1, | |
157 | XM_TX_PRMBL, 1, | |
158 | XM_AUTO_PAD, 1, | |
159 | XM_TXCRC, 1, | |
160 | XM_FCNTL, 1, | |
161 | XM_IPG, 0x3); | |
c1e5fcc9 | 162 | falcon_write(efx, ®, XM_TX_CFG_REG); |
8ceee660 BH |
163 | |
164 | /* Configure RX */ | |
165 | EFX_POPULATE_DWORD_5(reg, | |
166 | XM_RXEN, 1, | |
167 | XM_AUTO_DEPAD, 0, | |
168 | XM_ACPT_ALL_MCAST, 1, | |
169 | XM_ACPT_ALL_UCAST, efx->promiscuous, | |
170 | XM_PASS_CRC_ERR, 1); | |
c1e5fcc9 | 171 | falcon_write(efx, ®, XM_RX_CFG_REG); |
8ceee660 BH |
172 | |
173 | /* Set frame length */ | |
174 | max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); | |
175 | EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len); | |
c1e5fcc9 | 176 | falcon_write(efx, ®, XM_RX_PARAM_REG); |
8ceee660 BH |
177 | EFX_POPULATE_DWORD_2(reg, |
178 | XM_MAX_TX_FRM_SIZE, max_frame_len, | |
179 | XM_TX_JUMBO_MODE, 1); | |
c1e5fcc9 | 180 | falcon_write(efx, ®, XM_TX_PARAM_REG); |
8ceee660 BH |
181 | |
182 | EFX_POPULATE_DWORD_2(reg, | |
183 | XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ | |
dc8cfa55 | 184 | XM_DIS_FCNTL, !rx_fc); |
c1e5fcc9 | 185 | falcon_write(efx, ®, XM_FC_REG); |
8ceee660 BH |
186 | |
187 | /* Set MAC address */ | |
188 | EFX_POPULATE_DWORD_4(reg, | |
189 | XM_ADR_0, efx->net_dev->dev_addr[0], | |
190 | XM_ADR_1, efx->net_dev->dev_addr[1], | |
191 | XM_ADR_2, efx->net_dev->dev_addr[2], | |
192 | XM_ADR_3, efx->net_dev->dev_addr[3]); | |
c1e5fcc9 | 193 | falcon_write(efx, ®, XM_ADR_LO_REG); |
8ceee660 BH |
194 | EFX_POPULATE_DWORD_2(reg, |
195 | XM_ADR_4, efx->net_dev->dev_addr[4], | |
196 | XM_ADR_5, efx->net_dev->dev_addr[5]); | |
c1e5fcc9 | 197 | falcon_write(efx, ®, XM_ADR_HI_REG); |
8ceee660 BH |
198 | } |
199 | ||
3273c2e8 BH |
200 | static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) |
201 | { | |
c1e5fcc9 | 202 | efx_oword_t reg; |
dc8cfa55 BH |
203 | bool xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS); |
204 | bool xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI); | |
205 | bool xgmii_loopback = (efx->loopback_mode == LOOPBACK_XGMII); | |
3273c2e8 BH |
206 | |
207 | /* XGXS block is flaky and will need to be reset if moving | |
208 | * into our out of XGMII, XGXS or XAUI loopbacks. */ | |
209 | if (EFX_WORKAROUND_5147(efx)) { | |
dc8cfa55 BH |
210 | bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; |
211 | bool reset_xgxs; | |
3273c2e8 | 212 | |
c1e5fcc9 BH |
213 | falcon_read(efx, ®, XX_CORE_STAT_REG); |
214 | old_xgxs_loopback = EFX_OWORD_FIELD(reg, XX_XGXS_LB_EN); | |
215 | old_xgmii_loopback = EFX_OWORD_FIELD(reg, XX_XGMII_LB_EN); | |
3273c2e8 | 216 | |
c1e5fcc9 BH |
217 | falcon_read(efx, ®, XX_SD_CTL_REG); |
218 | old_xaui_loopback = EFX_OWORD_FIELD(reg, XX_LPBKA); | |
3273c2e8 BH |
219 | |
220 | /* The PHY driver may have turned XAUI off */ | |
221 | reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) || | |
222 | (xaui_loopback != old_xaui_loopback) || | |
223 | (xgmii_loopback != old_xgmii_loopback)); | |
8c8661e4 BH |
224 | |
225 | if (reset_xgxs) | |
226 | falcon_reset_xaui(efx); | |
3273c2e8 BH |
227 | } |
228 | ||
c1e5fcc9 BH |
229 | falcon_read(efx, ®, XX_CORE_STAT_REG); |
230 | EFX_SET_OWORD_FIELD(reg, XX_FORCE_SIG, | |
3273c2e8 BH |
231 | (xgxs_loopback || xaui_loopback) ? |
232 | XX_FORCE_SIG_DECODE_FORCED : 0); | |
c1e5fcc9 BH |
233 | EFX_SET_OWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback); |
234 | EFX_SET_OWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback); | |
235 | falcon_write(efx, ®, XX_CORE_STAT_REG); | |
236 | ||
237 | falcon_read(efx, ®, XX_SD_CTL_REG); | |
238 | EFX_SET_OWORD_FIELD(reg, XX_LPBKD, xaui_loopback); | |
239 | EFX_SET_OWORD_FIELD(reg, XX_LPBKC, xaui_loopback); | |
240 | EFX_SET_OWORD_FIELD(reg, XX_LPBKB, xaui_loopback); | |
241 | EFX_SET_OWORD_FIELD(reg, XX_LPBKA, xaui_loopback); | |
242 | falcon_write(efx, ®, XX_SD_CTL_REG); | |
3273c2e8 BH |
243 | } |
244 | ||
245 | ||
8ceee660 BH |
246 | /* Try and bring the Falcon side of the Falcon-Phy XAUI link fails |
247 | * to come back up. Bash it until it comes back up */ | |
177dfcd8 | 248 | static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries) |
8ceee660 | 249 | { |
177dfcd8 | 250 | efx->mac_up = falcon_xaui_link_ok(efx); |
8ceee660 | 251 | |
3273c2e8 | 252 | if ((efx->loopback_mode == LOOPBACK_NETWORK) || |
f8b87c17 | 253 | efx_phy_mode_disabled(efx->phy_mode)) |
177dfcd8 BH |
254 | /* XAUI link is expected to be down */ |
255 | return; | |
8ceee660 | 256 | |
177dfcd8 BH |
257 | while (!efx->mac_up && tries) { |
258 | EFX_LOG(efx, "bashing xaui\n"); | |
91ad757c | 259 | falcon_reset_xaui(efx); |
8ceee660 | 260 | udelay(200); |
8ceee660 | 261 | |
177dfcd8 BH |
262 | efx->mac_up = falcon_xaui_link_ok(efx); |
263 | --tries; | |
264 | } | |
8ceee660 BH |
265 | } |
266 | ||
177dfcd8 | 267 | static void falcon_reconfigure_xmac(struct efx_nic *efx) |
8ceee660 | 268 | { |
dc8cfa55 | 269 | falcon_mask_status_intr(efx, false); |
8ceee660 | 270 | |
3273c2e8 | 271 | falcon_reconfigure_xgxs_core(efx); |
8ceee660 | 272 | falcon_reconfigure_xmac_core(efx); |
3273c2e8 | 273 | |
8ceee660 BH |
274 | falcon_reconfigure_mac_wrapper(efx); |
275 | ||
177dfcd8 BH |
276 | falcon_check_xaui_link_up(efx, 5); |
277 | falcon_mask_status_intr(efx, true); | |
8ceee660 BH |
278 | } |
279 | ||
177dfcd8 | 280 | static void falcon_update_stats_xmac(struct efx_nic *efx) |
8ceee660 BH |
281 | { |
282 | struct efx_mac_stats *mac_stats = &efx->mac_stats; | |
283 | int rc; | |
284 | ||
285 | rc = falcon_dma_stats(efx, XgDmaDone_offset); | |
286 | if (rc) | |
287 | return; | |
288 | ||
289 | /* Update MAC stats from DMAed values */ | |
290 | FALCON_STAT(efx, XgRxOctets, rx_bytes); | |
291 | FALCON_STAT(efx, XgRxOctetsOK, rx_good_bytes); | |
292 | FALCON_STAT(efx, XgRxPkts, rx_packets); | |
293 | FALCON_STAT(efx, XgRxPktsOK, rx_good); | |
294 | FALCON_STAT(efx, XgRxBroadcastPkts, rx_broadcast); | |
295 | FALCON_STAT(efx, XgRxMulticastPkts, rx_multicast); | |
296 | FALCON_STAT(efx, XgRxUnicastPkts, rx_unicast); | |
297 | FALCON_STAT(efx, XgRxUndersizePkts, rx_lt64); | |
298 | FALCON_STAT(efx, XgRxOversizePkts, rx_gtjumbo); | |
299 | FALCON_STAT(efx, XgRxJabberPkts, rx_bad_gtjumbo); | |
300 | FALCON_STAT(efx, XgRxUndersizeFCSerrorPkts, rx_bad_lt64); | |
301 | FALCON_STAT(efx, XgRxDropEvents, rx_overflow); | |
302 | FALCON_STAT(efx, XgRxFCSerrorPkts, rx_bad); | |
303 | FALCON_STAT(efx, XgRxAlignError, rx_align_error); | |
304 | FALCON_STAT(efx, XgRxSymbolError, rx_symbol_error); | |
305 | FALCON_STAT(efx, XgRxInternalMACError, rx_internal_error); | |
306 | FALCON_STAT(efx, XgRxControlPkts, rx_control); | |
307 | FALCON_STAT(efx, XgRxPausePkts, rx_pause); | |
308 | FALCON_STAT(efx, XgRxPkts64Octets, rx_64); | |
309 | FALCON_STAT(efx, XgRxPkts65to127Octets, rx_65_to_127); | |
310 | FALCON_STAT(efx, XgRxPkts128to255Octets, rx_128_to_255); | |
311 | FALCON_STAT(efx, XgRxPkts256to511Octets, rx_256_to_511); | |
312 | FALCON_STAT(efx, XgRxPkts512to1023Octets, rx_512_to_1023); | |
313 | FALCON_STAT(efx, XgRxPkts1024to15xxOctets, rx_1024_to_15xx); | |
314 | FALCON_STAT(efx, XgRxPkts15xxtoMaxOctets, rx_15xx_to_jumbo); | |
315 | FALCON_STAT(efx, XgRxLengthError, rx_length_error); | |
316 | FALCON_STAT(efx, XgTxPkts, tx_packets); | |
317 | FALCON_STAT(efx, XgTxOctets, tx_bytes); | |
318 | FALCON_STAT(efx, XgTxMulticastPkts, tx_multicast); | |
319 | FALCON_STAT(efx, XgTxBroadcastPkts, tx_broadcast); | |
320 | FALCON_STAT(efx, XgTxUnicastPkts, tx_unicast); | |
321 | FALCON_STAT(efx, XgTxControlPkts, tx_control); | |
322 | FALCON_STAT(efx, XgTxPausePkts, tx_pause); | |
323 | FALCON_STAT(efx, XgTxPkts64Octets, tx_64); | |
324 | FALCON_STAT(efx, XgTxPkts65to127Octets, tx_65_to_127); | |
325 | FALCON_STAT(efx, XgTxPkts128to255Octets, tx_128_to_255); | |
326 | FALCON_STAT(efx, XgTxPkts256to511Octets, tx_256_to_511); | |
327 | FALCON_STAT(efx, XgTxPkts512to1023Octets, tx_512_to_1023); | |
328 | FALCON_STAT(efx, XgTxPkts1024to15xxOctets, tx_1024_to_15xx); | |
329 | FALCON_STAT(efx, XgTxPkts1519toMaxOctets, tx_15xx_to_jumbo); | |
330 | FALCON_STAT(efx, XgTxUndersizePkts, tx_lt64); | |
331 | FALCON_STAT(efx, XgTxOversizePkts, tx_gtjumbo); | |
332 | FALCON_STAT(efx, XgTxNonTcpUdpPkt, tx_non_tcpudp); | |
333 | FALCON_STAT(efx, XgTxMacSrcErrPkt, tx_mac_src_error); | |
334 | FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error); | |
335 | ||
336 | /* Update derived statistics */ | |
337 | mac_stats->tx_good_bytes = | |
c264361d BH |
338 | (mac_stats->tx_bytes - mac_stats->tx_bad_bytes - |
339 | mac_stats->tx_control * 64); | |
8ceee660 | 340 | mac_stats->rx_bad_bytes = |
c264361d BH |
341 | (mac_stats->rx_bytes - mac_stats->rx_good_bytes - |
342 | mac_stats->rx_control * 64); | |
8ceee660 BH |
343 | } |
344 | ||
177dfcd8 | 345 | static int falcon_check_xmac(struct efx_nic *efx) |
8ceee660 | 346 | { |
dc8cfa55 | 347 | bool xaui_link_ok; |
8ceee660 BH |
348 | int rc; |
349 | ||
3273c2e8 | 350 | if ((efx->loopback_mode == LOOPBACK_NETWORK) || |
f8b87c17 | 351 | efx_phy_mode_disabled(efx->phy_mode)) |
3273c2e8 BH |
352 | return 0; |
353 | ||
dc8cfa55 | 354 | falcon_mask_status_intr(efx, false); |
8ceee660 BH |
355 | xaui_link_ok = falcon_xaui_link_ok(efx); |
356 | ||
357 | if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok) | |
91ad757c | 358 | falcon_reset_xaui(efx); |
8ceee660 BH |
359 | |
360 | /* Call the PHY check_hw routine */ | |
361 | rc = efx->phy_op->check_hw(efx); | |
362 | ||
363 | /* Unmask interrupt if everything was (and still is) ok */ | |
364 | if (xaui_link_ok && efx->link_up) | |
dc8cfa55 | 365 | falcon_mask_status_intr(efx, true); |
8ceee660 BH |
366 | |
367 | return rc; | |
368 | } | |
369 | ||
177dfcd8 BH |
370 | struct efx_mac_operations falcon_xmac_operations = { |
371 | .reconfigure = falcon_reconfigure_xmac, | |
372 | .update_stats = falcon_update_stats_xmac, | |
373 | .check_hw = falcon_check_xmac, | |
374 | }; |