1098ca2bdSWarner Losh /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
4a94100faSBill Paul * Copyright (c) 1997, 1998-2003
5a94100faSBill Paul * Bill Paul <wpaul@windriver.com>. All rights reserved.
6a94100faSBill Paul *
7a94100faSBill Paul * Redistribution and use in source and binary forms, with or without
8a94100faSBill Paul * modification, are permitted provided that the following conditions
9a94100faSBill Paul * are met:
10a94100faSBill Paul * 1. Redistributions of source code must retain the above copyright
11a94100faSBill Paul * notice, this list of conditions and the following disclaimer.
12a94100faSBill Paul * 2. Redistributions in binary form must reproduce the above copyright
13a94100faSBill Paul * notice, this list of conditions and the following disclaimer in the
14a94100faSBill Paul * documentation and/or other materials provided with the distribution.
15a94100faSBill Paul * 3. All advertising materials mentioning features or use of this software
16a94100faSBill Paul * must display the following acknowledgement:
17a94100faSBill Paul * This product includes software developed by Bill Paul.
18a94100faSBill Paul * 4. Neither the name of the author nor the names of any co-contributors
19a94100faSBill Paul * may be used to endorse or promote products derived from this software
20a94100faSBill Paul * without specific prior written permission.
21a94100faSBill Paul *
22a94100faSBill Paul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23a94100faSBill Paul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24a94100faSBill Paul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25a94100faSBill Paul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26a94100faSBill Paul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27a94100faSBill Paul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28a94100faSBill Paul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29a94100faSBill Paul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30a94100faSBill Paul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31a94100faSBill Paul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32a94100faSBill Paul * THE POSSIBILITY OF SUCH DAMAGE.
33a94100faSBill Paul */
34a94100faSBill Paul
354dc52c32SDavid E. O'Brien #include <sys/cdefs.h>
36a94100faSBill Paul /*
37ed510fb0SBill Paul * RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver
38a94100faSBill Paul *
39a94100faSBill Paul * Written by Bill Paul <wpaul@windriver.com>
40a94100faSBill Paul * Senior Networking Software Engineer
41a94100faSBill Paul * Wind River Systems
42a94100faSBill Paul */
43a94100faSBill Paul
44a94100faSBill Paul /*
45a94100faSBill Paul * This driver is designed to support RealTek's next generation of
46a94100faSBill Paul * 10/100 and 10/100/1000 PCI ethernet controllers. There are currently
47ed510fb0SBill Paul * seven devices in this family: the RTL8139C+, the RTL8169, the RTL8169S,
48ed510fb0SBill Paul * RTL8110S, the RTL8168, the RTL8111 and the RTL8101E.
49a94100faSBill Paul *
50a94100faSBill Paul * The 8139C+ is a 10/100 ethernet chip. It is backwards compatible
51a94100faSBill Paul * with the older 8139 family, however it also supports a special
52a94100faSBill Paul * C+ mode of operation that provides several new performance enhancing
53a94100faSBill Paul * features. These include:
54a94100faSBill Paul *
55a94100faSBill Paul * o Descriptor based DMA mechanism. Each descriptor represents
56a94100faSBill Paul * a single packet fragment. Data buffers may be aligned on
57a94100faSBill Paul * any byte boundary.
58a94100faSBill Paul *
59a94100faSBill Paul * o 64-bit DMA
60a94100faSBill Paul *
61a94100faSBill Paul * o TCP/IP checksum offload for both RX and TX
62a94100faSBill Paul *
63a94100faSBill Paul * o High and normal priority transmit DMA rings
64a94100faSBill Paul *
65a94100faSBill Paul * o VLAN tag insertion and extraction
66a94100faSBill Paul *
67a94100faSBill Paul * o TCP large send (segmentation offload)
68a94100faSBill Paul *
69a94100faSBill Paul * Like the 8139, the 8139C+ also has a built-in 10/100 PHY. The C+
70a94100faSBill Paul * programming API is fairly straightforward. The RX filtering, EEPROM
71a94100faSBill Paul * access and PHY access is the same as it is on the older 8139 series
72a94100faSBill Paul * chips.
73a94100faSBill Paul *
74a94100faSBill Paul * The 8169 is a 64-bit 10/100/1000 gigabit ethernet MAC. It has almost the
75a94100faSBill Paul * same programming API and feature set as the 8139C+ with the following
76a94100faSBill Paul * differences and additions:
77a94100faSBill Paul *
78a94100faSBill Paul * o 1000Mbps mode
79a94100faSBill Paul *
80a94100faSBill Paul * o Jumbo frames
81a94100faSBill Paul *
82a94100faSBill Paul * o GMII and TBI ports/registers for interfacing with copper
83a94100faSBill Paul * or fiber PHYs
84a94100faSBill Paul *
85a94100faSBill Paul * o RX and TX DMA rings can have up to 1024 descriptors
86a94100faSBill Paul * (the 8139C+ allows a maximum of 64)
87a94100faSBill Paul *
88a94100faSBill Paul * o Slight differences in register layout from the 8139C+
89a94100faSBill Paul *
90a94100faSBill Paul * The TX start and timer interrupt registers are at different locations
91a94100faSBill Paul * on the 8169 than they are on the 8139C+. Also, the status word in the
92a94100faSBill Paul * RX descriptor has a slightly different bit layout. The 8169 does not
93a94100faSBill Paul * have a built-in PHY. Most reference boards use a Marvell 88E1000 'Alaska'
94a94100faSBill Paul * copper gigE PHY.
95a94100faSBill Paul *
96a94100faSBill Paul * The 8169S/8110S 10/100/1000 devices have built-in copper gigE PHYs
97a94100faSBill Paul * (the 'S' stands for 'single-chip'). These devices have the same
98a94100faSBill Paul * programming API as the older 8169, but also have some vendor-specific
99a94100faSBill Paul * registers for the on-board PHY. The 8110S is a LAN-on-motherboard
100a94100faSBill Paul * part designed to be pin-compatible with the RealTek 8100 10/100 chip.
101a94100faSBill Paul *
102a94100faSBill Paul * This driver takes advantage of the RX and TX checksum offload and
103a94100faSBill Paul * VLAN tag insertion/extraction features. It also implements TX
104a94100faSBill Paul * interrupt moderation using the timer interrupt registers, which
105a94100faSBill Paul * significantly reduces TX interrupt load. There is also support
106a94100faSBill Paul * for jumbo frames, however the 8169/8169S/8110S can not transmit
10722a11c96SJohn-Mark Gurney * jumbo frames larger than 7440, so the max MTU possible with this
10822a11c96SJohn-Mark Gurney * driver is 7422 bytes.
109a94100faSBill Paul */
110a94100faSBill Paul
111f0796cd2SGleb Smirnoff #ifdef HAVE_KERNEL_OPTION_HEADERS
112f0796cd2SGleb Smirnoff #include "opt_device_polling.h"
113f0796cd2SGleb Smirnoff #endif
114f0796cd2SGleb Smirnoff
115a94100faSBill Paul #include <sys/param.h>
116a94100faSBill Paul #include <sys/endian.h>
117a94100faSBill Paul #include <sys/systm.h>
118a94100faSBill Paul #include <sys/sockio.h>
119a94100faSBill Paul #include <sys/mbuf.h>
120a94100faSBill Paul #include <sys/malloc.h>
121fe12f24bSPoul-Henning Kamp #include <sys/module.h>
122a94100faSBill Paul #include <sys/kernel.h>
123a94100faSBill Paul #include <sys/socket.h>
124ed510fb0SBill Paul #include <sys/lock.h>
125ed510fb0SBill Paul #include <sys/mutex.h>
1260534aae0SPyun YongHyeon #include <sys/sysctl.h>
127ed510fb0SBill Paul #include <sys/taskqueue.h>
128a94100faSBill Paul
1297790c8c1SConrad Meyer #include <net/debugnet.h>
130a94100faSBill Paul #include <net/if.h>
13176039bc8SGleb Smirnoff #include <net/if_var.h>
132a94100faSBill Paul #include <net/if_arp.h>
133a94100faSBill Paul #include <net/ethernet.h>
134a94100faSBill Paul #include <net/if_dl.h>
135a94100faSBill Paul #include <net/if_media.h>
136fc74a9f9SBrooks Davis #include <net/if_types.h>
137a94100faSBill Paul #include <net/if_vlan_var.h>
138a94100faSBill Paul
139a94100faSBill Paul #include <net/bpf.h>
140a94100faSBill Paul
141a94100faSBill Paul #include <machine/bus.h>
142a94100faSBill Paul #include <machine/resource.h>
143a94100faSBill Paul #include <sys/bus.h>
144a94100faSBill Paul #include <sys/rman.h>
145a94100faSBill Paul
146a94100faSBill Paul #include <dev/mii/mii.h>
147a94100faSBill Paul #include <dev/mii/miivar.h>
148a94100faSBill Paul
149a94100faSBill Paul #include <dev/pci/pcireg.h>
150a94100faSBill Paul #include <dev/pci/pcivar.h>
151a94100faSBill Paul
152b2d3d26fSGleb Smirnoff #include <dev/rl/if_rlreg.h>
153d65abd66SPyun YongHyeon
154a94100faSBill Paul MODULE_DEPEND(re, pci, 1, 1, 1);
155a94100faSBill Paul MODULE_DEPEND(re, ether, 1, 1, 1);
156a94100faSBill Paul MODULE_DEPEND(re, miibus, 1, 1, 1);
157a94100faSBill Paul
158298bfdf3SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */
159a94100faSBill Paul #include "miibus_if.h"
160a94100faSBill Paul
1615774c5ffSPyun YongHyeon /* Tunables. */
162502be0f7SPyun YongHyeon static int intr_filter = 0;
163502be0f7SPyun YongHyeon TUNABLE_INT("hw.re.intr_filter", &intr_filter);
164c2d2e19cSPyun YongHyeon static int msi_disable = 0;
1655774c5ffSPyun YongHyeon TUNABLE_INT("hw.re.msi_disable", &msi_disable);
1664a58fd45SPyun YongHyeon static int msix_disable = 0;
1674a58fd45SPyun YongHyeon TUNABLE_INT("hw.re.msix_disable", &msix_disable);
1682c21710bSPyun YongHyeon static int prefer_iomap = 0;
1692c21710bSPyun YongHyeon TUNABLE_INT("hw.re.prefer_iomap", &prefer_iomap);
1705774c5ffSPyun YongHyeon
171a94100faSBill Paul #define RE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
172a94100faSBill Paul
173a94100faSBill Paul /*
174a94100faSBill Paul * Various supported device vendors/types and their names.
175a94100faSBill Paul */
17629658c96SDimitry Andric static const struct rl_type re_devs[] = {
1779dfcacbeSPyun YongHyeon { DLINK_VENDORID, DLINK_DEVICEID_528T, 0,
17832aa5f0eSAnton Berezin "D-Link DGE-528(T) Gigabit Ethernet Adapter" },
179caa19d50SPyun YongHyeon { DLINK_VENDORID, DLINK_DEVICEID_530T_REVC, 0,
180caa19d50SPyun YongHyeon "D-Link DGE-530(T) Gigabit Ethernet Adapter" },
1813c871489SSk Razee { RT_VENDORID, RT_DEVICEID_2600, 0,
1823c871489SSk Razee "RealTek Killer E2600 Gigabit Ethernet Controller" },
1839dfcacbeSPyun YongHyeon { RT_VENDORID, RT_DEVICEID_8139, 0,
184a94100faSBill Paul "RealTek 8139C+ 10/100BaseTX" },
1859dfcacbeSPyun YongHyeon { RT_VENDORID, RT_DEVICEID_8101E, 0,
18654899a96SPyun YongHyeon "RealTek 810xE PCIe 10/100baseTX" },
1879dfcacbeSPyun YongHyeon { RT_VENDORID, RT_DEVICEID_8168, 0,
188ab9f923eSPyun YongHyeon "RealTek 8168/8111 B/C/CP/D/DP/E/F/G PCIe Gigabit Ethernet" },
189ce3e137cSMark Johnston { RT_VENDORID, RT_DEVICEID_8161, 0,
190ce3e137cSMark Johnston "RealTek 8168 Gigabit Ethernet" },
191938e9a89SKevin Lo { NCUBE_VENDORID, RT_DEVICEID_8168, 0,
192938e9a89SKevin Lo "TP-Link TG-3468 v2 (RTL8168) Gigabit Ethernet" },
1939dfcacbeSPyun YongHyeon { RT_VENDORID, RT_DEVICEID_8169, 0,
194715922d7SPyun YongHyeon "RealTek 8169/8169S/8169SB(L)/8110S/8110SB(L) Gigabit Ethernet" },
1959dfcacbeSPyun YongHyeon { RT_VENDORID, RT_DEVICEID_8169SC, 0,
1962ee2c3b4SRemko Lodder "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" },
1979dfcacbeSPyun YongHyeon { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, 0,
198ea263191SMIHIRA Sanpei Yoshiro "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" },
1999dfcacbeSPyun YongHyeon { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, 0,
20026390635SJohn Baldwin "Linksys EG1032 (RTL8169S) Gigabit Ethernet" },
2019dfcacbeSPyun YongHyeon { USR_VENDORID, USR_DEVICEID_997902, 0,
202dfdb409eSPyun YongHyeon "US Robotics 997902 (RTL8169S) Gigabit Ethernet" }
203a94100faSBill Paul };
204a94100faSBill Paul
20529658c96SDimitry Andric static const struct rl_hwrev re_hwrevs[] = {
20681eee0ebSPyun YongHyeon { RL_HWREV_8139, RL_8139, "", RL_MTU },
20781eee0ebSPyun YongHyeon { RL_HWREV_8139A, RL_8139, "A", RL_MTU },
20881eee0ebSPyun YongHyeon { RL_HWREV_8139AG, RL_8139, "A-G", RL_MTU },
20981eee0ebSPyun YongHyeon { RL_HWREV_8139B, RL_8139, "B", RL_MTU },
21081eee0ebSPyun YongHyeon { RL_HWREV_8130, RL_8139, "8130", RL_MTU },
21181eee0ebSPyun YongHyeon { RL_HWREV_8139C, RL_8139, "C", RL_MTU },
21281eee0ebSPyun YongHyeon { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C", RL_MTU },
21381eee0ebSPyun YongHyeon { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+", RL_MTU },
214ef278cb4SPyun YongHyeon { RL_HWREV_8168B_SPIN1, RL_8169, "8168", RL_JUMBO_MTU },
21581eee0ebSPyun YongHyeon { RL_HWREV_8169, RL_8169, "8169", RL_JUMBO_MTU },
21681eee0ebSPyun YongHyeon { RL_HWREV_8169S, RL_8169, "8169S", RL_JUMBO_MTU },
21781eee0ebSPyun YongHyeon { RL_HWREV_8110S, RL_8169, "8110S", RL_JUMBO_MTU },
21881eee0ebSPyun YongHyeon { RL_HWREV_8169_8110SB, RL_8169, "8169SB/8110SB", RL_JUMBO_MTU },
21981eee0ebSPyun YongHyeon { RL_HWREV_8169_8110SC, RL_8169, "8169SC/8110SC", RL_JUMBO_MTU },
22081eee0ebSPyun YongHyeon { RL_HWREV_8169_8110SBL, RL_8169, "8169SBL/8110SBL", RL_JUMBO_MTU },
22181eee0ebSPyun YongHyeon { RL_HWREV_8169_8110SCE, RL_8169, "8169SC/8110SC", RL_JUMBO_MTU },
22281eee0ebSPyun YongHyeon { RL_HWREV_8100, RL_8139, "8100", RL_MTU },
22381eee0ebSPyun YongHyeon { RL_HWREV_8101, RL_8139, "8101", RL_MTU },
22481eee0ebSPyun YongHyeon { RL_HWREV_8100E, RL_8169, "8100E", RL_MTU },
22581eee0ebSPyun YongHyeon { RL_HWREV_8101E, RL_8169, "8101E", RL_MTU },
22681eee0ebSPyun YongHyeon { RL_HWREV_8102E, RL_8169, "8102E", RL_MTU },
22781eee0ebSPyun YongHyeon { RL_HWREV_8102EL, RL_8169, "8102EL", RL_MTU },
22881eee0ebSPyun YongHyeon { RL_HWREV_8102EL_SPIN1, RL_8169, "8102EL", RL_MTU },
22981eee0ebSPyun YongHyeon { RL_HWREV_8103E, RL_8169, "8103E", RL_MTU },
23039e69201SPyun YongHyeon { RL_HWREV_8401E, RL_8169, "8401E", RL_MTU },
231a9e3362aSPyun YongHyeon { RL_HWREV_8402, RL_8169, "8402", RL_MTU },
23254899a96SPyun YongHyeon { RL_HWREV_8105E, RL_8169, "8105E", RL_MTU },
2336b0a8e04SPyun YongHyeon { RL_HWREV_8105E_SPIN1, RL_8169, "8105E", RL_MTU },
234214c71f6SPyun YongHyeon { RL_HWREV_8106E, RL_8169, "8106E", RL_MTU },
235ef278cb4SPyun YongHyeon { RL_HWREV_8168B_SPIN2, RL_8169, "8168", RL_JUMBO_MTU },
236ef278cb4SPyun YongHyeon { RL_HWREV_8168B_SPIN3, RL_8169, "8168", RL_JUMBO_MTU },
23781eee0ebSPyun YongHyeon { RL_HWREV_8168C, RL_8169, "8168C/8111C", RL_JUMBO_MTU_6K },
23881eee0ebSPyun YongHyeon { RL_HWREV_8168C_SPIN2, RL_8169, "8168C/8111C", RL_JUMBO_MTU_6K },
23981eee0ebSPyun YongHyeon { RL_HWREV_8168CP, RL_8169, "8168CP/8111CP", RL_JUMBO_MTU_6K },
24081eee0ebSPyun YongHyeon { RL_HWREV_8168D, RL_8169, "8168D/8111D", RL_JUMBO_MTU_9K },
24181eee0ebSPyun YongHyeon { RL_HWREV_8168DP, RL_8169, "8168DP/8111DP", RL_JUMBO_MTU_9K },
24281eee0ebSPyun YongHyeon { RL_HWREV_8168E, RL_8169, "8168E/8111E", RL_JUMBO_MTU_9K},
24381eee0ebSPyun YongHyeon { RL_HWREV_8168E_VL, RL_8169, "8168E/8111E-VL", RL_JUMBO_MTU_6K},
244c3767eabSPyun YongHyeon { RL_HWREV_8168EP, RL_8169, "8168EP/8111EP", RL_JUMBO_MTU_9K},
245d467ffaaSPyun YongHyeon { RL_HWREV_8168F, RL_8169, "8168F/8111F", RL_JUMBO_MTU_9K},
24688d2b69cSBrad Smith { RL_HWREV_8168FP, RL_8169, "8168FP/8111FP", RL_JUMBO_MTU_9K},
247ab9f923eSPyun YongHyeon { RL_HWREV_8168G, RL_8169, "8168G/8111G", RL_JUMBO_MTU_9K},
248ab9f923eSPyun YongHyeon { RL_HWREV_8168GU, RL_8169, "8168GU/8111GU", RL_JUMBO_MTU_9K},
24996b2c26aSMarius Strobl { RL_HWREV_8168H, RL_8169, "8168H/8111H", RL_JUMBO_MTU_9K},
250d56f7f52SPyun YongHyeon { RL_HWREV_8411, RL_8169, "8411", RL_JUMBO_MTU_9K},
251ab9f923eSPyun YongHyeon { RL_HWREV_8411B, RL_8169, "8411B", RL_JUMBO_MTU_9K},
25281eee0ebSPyun YongHyeon { 0, 0, NULL, 0 }
253a94100faSBill Paul };
254a94100faSBill Paul
255a94100faSBill Paul static int re_probe (device_t);
256a94100faSBill Paul static int re_attach (device_t);
257a94100faSBill Paul static int re_detach (device_t);
258a94100faSBill Paul
259d65abd66SPyun YongHyeon static int re_encap (struct rl_softc *, struct mbuf **);
260a94100faSBill Paul
261a94100faSBill Paul static void re_dma_map_addr (void *, bus_dma_segment_t *, int, int);
262a94100faSBill Paul static int re_allocmem (device_t, struct rl_softc *);
263d65abd66SPyun YongHyeon static __inline void re_discard_rxbuf
264d65abd66SPyun YongHyeon (struct rl_softc *, int);
265d65abd66SPyun YongHyeon static int re_newbuf (struct rl_softc *, int);
26681eee0ebSPyun YongHyeon static int re_jumbo_newbuf (struct rl_softc *, int);
267a94100faSBill Paul static int re_rx_list_init (struct rl_softc *);
26881eee0ebSPyun YongHyeon static int re_jrx_list_init (struct rl_softc *);
269a94100faSBill Paul static int re_tx_list_init (struct rl_softc *);
27022a11c96SJohn-Mark Gurney #ifdef RE_FIXUP_RX
27122a11c96SJohn-Mark Gurney static __inline void re_fixup_rx
27222a11c96SJohn-Mark Gurney (struct mbuf *);
27322a11c96SJohn-Mark Gurney #endif
2741abcdbd1SAttilio Rao static int re_rxeof (struct rl_softc *, int *);
275a94100faSBill Paul static void re_txeof (struct rl_softc *);
27697b9d4baSJohn-Mark Gurney #ifdef DEVICE_POLLING
2774519a073SJustin Hibbits static int re_poll (if_t, enum poll_cmd, int);
2784519a073SJustin Hibbits static int re_poll_locked (if_t, enum poll_cmd, int);
27997b9d4baSJohn-Mark Gurney #endif
280ef544f63SPaolo Pisati static int re_intr (void *);
281502be0f7SPyun YongHyeon static void re_intr_msi (void *);
282a94100faSBill Paul static void re_tick (void *);
283ed510fb0SBill Paul static void re_int_task (void *, int);
2844519a073SJustin Hibbits static void re_start (if_t);
2854519a073SJustin Hibbits static void re_start_locked (if_t);
286306c97e2SMark Johnston static void re_start_tx (struct rl_softc *);
2874519a073SJustin Hibbits static int re_ioctl (if_t, u_long, caddr_t);
288a94100faSBill Paul static void re_init (void *);
28997b9d4baSJohn-Mark Gurney static void re_init_locked (struct rl_softc *);
290a94100faSBill Paul static void re_stop (struct rl_softc *);
2911d545c7aSMarius Strobl static void re_watchdog (struct rl_softc *);
292a94100faSBill Paul static int re_suspend (device_t);
293a94100faSBill Paul static int re_resume (device_t);
2946a087a87SPyun YongHyeon static int re_shutdown (device_t);
2954519a073SJustin Hibbits static int re_ifmedia_upd (if_t);
2964519a073SJustin Hibbits static void re_ifmedia_sts (if_t, struct ifmediareq *);
297a94100faSBill Paul
298a94100faSBill Paul static void re_eeprom_putbyte (struct rl_softc *, int);
299a94100faSBill Paul static void re_eeprom_getword (struct rl_softc *, int, u_int16_t *);
300ed510fb0SBill Paul static void re_read_eeprom (struct rl_softc *, caddr_t, int, int);
301a94100faSBill Paul static int re_gmii_readreg (device_t, int, int);
302a94100faSBill Paul static int re_gmii_writereg (device_t, int, int, int);
303a94100faSBill Paul
304a94100faSBill Paul static int re_miibus_readreg (device_t, int, int);
305a94100faSBill Paul static int re_miibus_writereg (device_t, int, int, int);
306a94100faSBill Paul static void re_miibus_statchg (device_t);
307a94100faSBill Paul
30881eee0ebSPyun YongHyeon static void re_set_jumbo (struct rl_softc *, int);
309ff191365SJung-uk Kim static void re_set_rxmode (struct rl_softc *);
310a94100faSBill Paul static void re_reset (struct rl_softc *);
3117467bd53SPyun YongHyeon static void re_setwol (struct rl_softc *);
3127467bd53SPyun YongHyeon static void re_clrwol (struct rl_softc *);
3136830588dSPyun YongHyeon static void re_set_linkspeed (struct rl_softc *);
314a94100faSBill Paul
3157790c8c1SConrad Meyer DEBUGNET_DEFINE(re);
316306c97e2SMark Johnston
317579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP /* see ixgbe.c for details */
318579a6e3cSLuigi Rizzo #include <dev/netmap/if_re_netmap.h>
319847bf383SLuigi Rizzo MODULE_DEPEND(re, netmap, 1, 1, 1);
320579a6e3cSLuigi Rizzo #endif /* !DEV_NETMAP */
321579a6e3cSLuigi Rizzo
322ed510fb0SBill Paul #ifdef RE_DIAG
323a94100faSBill Paul static int re_diag (struct rl_softc *);
324ed510fb0SBill Paul #endif
325a94100faSBill Paul
3260534aae0SPyun YongHyeon static void re_add_sysctls (struct rl_softc *);
3270534aae0SPyun YongHyeon static int re_sysctl_stats (SYSCTL_HANDLER_ARGS);
328502be0f7SPyun YongHyeon static int sysctl_int_range (SYSCTL_HANDLER_ARGS, int, int);
329502be0f7SPyun YongHyeon static int sysctl_hw_re_int_mod (SYSCTL_HANDLER_ARGS);
3300534aae0SPyun YongHyeon
331a94100faSBill Paul static device_method_t re_methods[] = {
332a94100faSBill Paul /* Device interface */
333a94100faSBill Paul DEVMETHOD(device_probe, re_probe),
334a94100faSBill Paul DEVMETHOD(device_attach, re_attach),
335a94100faSBill Paul DEVMETHOD(device_detach, re_detach),
336a94100faSBill Paul DEVMETHOD(device_suspend, re_suspend),
337a94100faSBill Paul DEVMETHOD(device_resume, re_resume),
338a94100faSBill Paul DEVMETHOD(device_shutdown, re_shutdown),
339a94100faSBill Paul
340a94100faSBill Paul /* MII interface */
341a94100faSBill Paul DEVMETHOD(miibus_readreg, re_miibus_readreg),
342a94100faSBill Paul DEVMETHOD(miibus_writereg, re_miibus_writereg),
343a94100faSBill Paul DEVMETHOD(miibus_statchg, re_miibus_statchg),
344a94100faSBill Paul
3454b7ec270SMarius Strobl DEVMETHOD_END
346a94100faSBill Paul };
347a94100faSBill Paul
348a94100faSBill Paul static driver_t re_driver = {
349a94100faSBill Paul "re",
350a94100faSBill Paul re_methods,
351a94100faSBill Paul sizeof(struct rl_softc)
352a94100faSBill Paul };
353a94100faSBill Paul
35430c024c5SJohn Baldwin DRIVER_MODULE(re, pci, re_driver, 0, 0);
3553e38757dSJohn Baldwin DRIVER_MODULE(miibus, re, miibus_driver, 0, 0);
356a94100faSBill Paul
357a94100faSBill Paul #define EE_SET(x) \
358a94100faSBill Paul CSR_WRITE_1(sc, RL_EECMD, \
359a94100faSBill Paul CSR_READ_1(sc, RL_EECMD) | x)
360a94100faSBill Paul
361a94100faSBill Paul #define EE_CLR(x) \
362a94100faSBill Paul CSR_WRITE_1(sc, RL_EECMD, \
363a94100faSBill Paul CSR_READ_1(sc, RL_EECMD) & ~x)
364a94100faSBill Paul
365a94100faSBill Paul /*
366a94100faSBill Paul * Send a read command and address to the EEPROM, check for ACK.
367a94100faSBill Paul */
368a94100faSBill Paul static void
re_eeprom_putbyte(struct rl_softc * sc,int addr)3697b5ffebfSPyun YongHyeon re_eeprom_putbyte(struct rl_softc *sc, int addr)
370a94100faSBill Paul {
3710ce0868aSPyun YongHyeon int d, i;
372a94100faSBill Paul
373ed510fb0SBill Paul d = addr | (RL_9346_READ << sc->rl_eewidth);
374a94100faSBill Paul
375a94100faSBill Paul /*
376a94100faSBill Paul * Feed in each bit and strobe the clock.
377a94100faSBill Paul */
378ed510fb0SBill Paul
379ed510fb0SBill Paul for (i = 1 << (sc->rl_eewidth + 3); i; i >>= 1) {
380a94100faSBill Paul if (d & i) {
381a94100faSBill Paul EE_SET(RL_EE_DATAIN);
382a94100faSBill Paul } else {
383a94100faSBill Paul EE_CLR(RL_EE_DATAIN);
384a94100faSBill Paul }
385a94100faSBill Paul DELAY(100);
386a94100faSBill Paul EE_SET(RL_EE_CLK);
387a94100faSBill Paul DELAY(150);
388a94100faSBill Paul EE_CLR(RL_EE_CLK);
389a94100faSBill Paul DELAY(100);
390a94100faSBill Paul }
391a94100faSBill Paul }
392a94100faSBill Paul
393a94100faSBill Paul /*
394a94100faSBill Paul * Read a word of data stored in the EEPROM at address 'addr.'
395a94100faSBill Paul */
396a94100faSBill Paul static void
re_eeprom_getword(struct rl_softc * sc,int addr,u_int16_t * dest)3977b5ffebfSPyun YongHyeon re_eeprom_getword(struct rl_softc *sc, int addr, u_int16_t *dest)
398a94100faSBill Paul {
3990ce0868aSPyun YongHyeon int i;
400a94100faSBill Paul u_int16_t word = 0;
401a94100faSBill Paul
402a94100faSBill Paul /*
403a94100faSBill Paul * Send address of word we want to read.
404a94100faSBill Paul */
405a94100faSBill Paul re_eeprom_putbyte(sc, addr);
406a94100faSBill Paul
407a94100faSBill Paul /*
408a94100faSBill Paul * Start reading bits from EEPROM.
409a94100faSBill Paul */
410a94100faSBill Paul for (i = 0x8000; i; i >>= 1) {
411a94100faSBill Paul EE_SET(RL_EE_CLK);
412a94100faSBill Paul DELAY(100);
413a94100faSBill Paul if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT)
414a94100faSBill Paul word |= i;
415a94100faSBill Paul EE_CLR(RL_EE_CLK);
416a94100faSBill Paul DELAY(100);
417a94100faSBill Paul }
418a94100faSBill Paul
419a94100faSBill Paul *dest = word;
420a94100faSBill Paul }
421a94100faSBill Paul
422a94100faSBill Paul /*
423a94100faSBill Paul * Read a sequence of words from the EEPROM.
424a94100faSBill Paul */
425a94100faSBill Paul static void
re_read_eeprom(struct rl_softc * sc,caddr_t dest,int off,int cnt)4267b5ffebfSPyun YongHyeon re_read_eeprom(struct rl_softc *sc, caddr_t dest, int off, int cnt)
427a94100faSBill Paul {
428a94100faSBill Paul int i;
429a94100faSBill Paul u_int16_t word = 0, *ptr;
430a94100faSBill Paul
431ed510fb0SBill Paul CSR_SETBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM);
432ed510fb0SBill Paul
433ed510fb0SBill Paul DELAY(100);
434ed510fb0SBill Paul
435a94100faSBill Paul for (i = 0; i < cnt; i++) {
436ed510fb0SBill Paul CSR_SETBIT_1(sc, RL_EECMD, RL_EE_SEL);
437a94100faSBill Paul re_eeprom_getword(sc, off + i, &word);
438ed510fb0SBill Paul CSR_CLRBIT_1(sc, RL_EECMD, RL_EE_SEL);
439a94100faSBill Paul ptr = (u_int16_t *)(dest + (i * 2));
440be099007SPyun YongHyeon *ptr = word;
441a94100faSBill Paul }
442ed510fb0SBill Paul
443ed510fb0SBill Paul CSR_CLRBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM);
444a94100faSBill Paul }
445a94100faSBill Paul
446a94100faSBill Paul static int
re_gmii_readreg(device_t dev,int phy,int reg)4477b5ffebfSPyun YongHyeon re_gmii_readreg(device_t dev, int phy, int reg)
448a94100faSBill Paul {
449a94100faSBill Paul struct rl_softc *sc;
450a94100faSBill Paul u_int32_t rval;
451a94100faSBill Paul int i;
452a94100faSBill Paul
453a94100faSBill Paul sc = device_get_softc(dev);
454a94100faSBill Paul
4559bac70b8SBill Paul /* Let the rgephy driver read the GMEDIASTAT register */
4569bac70b8SBill Paul
4579bac70b8SBill Paul if (reg == RL_GMEDIASTAT) {
4589bac70b8SBill Paul rval = CSR_READ_1(sc, RL_GMEDIASTAT);
4599bac70b8SBill Paul return (rval);
4609bac70b8SBill Paul }
4619bac70b8SBill Paul
462a94100faSBill Paul CSR_WRITE_4(sc, RL_PHYAR, reg << 16);
463a94100faSBill Paul
46496b774f4SPyun YongHyeon for (i = 0; i < RL_PHY_TIMEOUT; i++) {
465a94100faSBill Paul rval = CSR_READ_4(sc, RL_PHYAR);
466a94100faSBill Paul if (rval & RL_PHYAR_BUSY)
467a94100faSBill Paul break;
4682bc085c6SPyun YongHyeon DELAY(25);
469a94100faSBill Paul }
470a94100faSBill Paul
47196b774f4SPyun YongHyeon if (i == RL_PHY_TIMEOUT) {
4726b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "PHY read failed\n");
473a94100faSBill Paul return (0);
474a94100faSBill Paul }
475a94100faSBill Paul
4762bc085c6SPyun YongHyeon /*
4772bc085c6SPyun YongHyeon * Controller requires a 20us delay to process next MDIO request.
4782bc085c6SPyun YongHyeon */
4792bc085c6SPyun YongHyeon DELAY(20);
4802bc085c6SPyun YongHyeon
481a94100faSBill Paul return (rval & RL_PHYAR_PHYDATA);
482a94100faSBill Paul }
483a94100faSBill Paul
484a94100faSBill Paul static int
re_gmii_writereg(device_t dev,int phy,int reg,int data)4857b5ffebfSPyun YongHyeon re_gmii_writereg(device_t dev, int phy, int reg, int data)
486a94100faSBill Paul {
487a94100faSBill Paul struct rl_softc *sc;
488a94100faSBill Paul u_int32_t rval;
489a94100faSBill Paul int i;
490a94100faSBill Paul
491a94100faSBill Paul sc = device_get_softc(dev);
492a94100faSBill Paul
493a94100faSBill Paul CSR_WRITE_4(sc, RL_PHYAR, (reg << 16) |
4949bac70b8SBill Paul (data & RL_PHYAR_PHYDATA) | RL_PHYAR_BUSY);
495a94100faSBill Paul
49696b774f4SPyun YongHyeon for (i = 0; i < RL_PHY_TIMEOUT; i++) {
497a94100faSBill Paul rval = CSR_READ_4(sc, RL_PHYAR);
498a94100faSBill Paul if (!(rval & RL_PHYAR_BUSY))
499a94100faSBill Paul break;
5002bc085c6SPyun YongHyeon DELAY(25);
501a94100faSBill Paul }
502a94100faSBill Paul
50396b774f4SPyun YongHyeon if (i == RL_PHY_TIMEOUT) {
5046b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "PHY write failed\n");
505a94100faSBill Paul return (0);
506a94100faSBill Paul }
507a94100faSBill Paul
5082bc085c6SPyun YongHyeon /*
5092bc085c6SPyun YongHyeon * Controller requires a 20us delay to process next MDIO request.
5102bc085c6SPyun YongHyeon */
5112bc085c6SPyun YongHyeon DELAY(20);
5122bc085c6SPyun YongHyeon
513a94100faSBill Paul return (0);
514a94100faSBill Paul }
515a94100faSBill Paul
516a94100faSBill Paul static int
re_miibus_readreg(device_t dev,int phy,int reg)5177b5ffebfSPyun YongHyeon re_miibus_readreg(device_t dev, int phy, int reg)
518a94100faSBill Paul {
519a94100faSBill Paul struct rl_softc *sc;
520a94100faSBill Paul u_int16_t rval = 0;
521a94100faSBill Paul u_int16_t re8139_reg = 0;
522a94100faSBill Paul
523a94100faSBill Paul sc = device_get_softc(dev);
524a94100faSBill Paul
525a94100faSBill Paul if (sc->rl_type == RL_8169) {
526a94100faSBill Paul rval = re_gmii_readreg(dev, phy, reg);
527a94100faSBill Paul return (rval);
528a94100faSBill Paul }
529a94100faSBill Paul
530a94100faSBill Paul switch (reg) {
531a94100faSBill Paul case MII_BMCR:
532a94100faSBill Paul re8139_reg = RL_BMCR;
533a94100faSBill Paul break;
534a94100faSBill Paul case MII_BMSR:
535a94100faSBill Paul re8139_reg = RL_BMSR;
536a94100faSBill Paul break;
537a94100faSBill Paul case MII_ANAR:
538a94100faSBill Paul re8139_reg = RL_ANAR;
539a94100faSBill Paul break;
540a94100faSBill Paul case MII_ANER:
541a94100faSBill Paul re8139_reg = RL_ANER;
542a94100faSBill Paul break;
543a94100faSBill Paul case MII_ANLPAR:
544a94100faSBill Paul re8139_reg = RL_LPAR;
545a94100faSBill Paul break;
546a94100faSBill Paul case MII_PHYIDR1:
547a94100faSBill Paul case MII_PHYIDR2:
548a94100faSBill Paul return (0);
549a94100faSBill Paul /*
550a94100faSBill Paul * Allow the rlphy driver to read the media status
551a94100faSBill Paul * register. If we have a link partner which does not
552a94100faSBill Paul * support NWAY, this is the register which will tell
553a94100faSBill Paul * us the results of parallel detection.
554a94100faSBill Paul */
555a94100faSBill Paul case RL_MEDIASTAT:
556a94100faSBill Paul rval = CSR_READ_1(sc, RL_MEDIASTAT);
557a94100faSBill Paul return (rval);
558a94100faSBill Paul default:
5596b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "bad phy register\n");
560a94100faSBill Paul return (0);
561a94100faSBill Paul }
562a94100faSBill Paul rval = CSR_READ_2(sc, re8139_reg);
563baa12772SPyun YongHyeon if (sc->rl_type == RL_8139CPLUS && re8139_reg == RL_BMCR) {
564baa12772SPyun YongHyeon /* 8139C+ has different bit layout. */
565baa12772SPyun YongHyeon rval &= ~(BMCR_LOOP | BMCR_ISO);
566baa12772SPyun YongHyeon }
567a94100faSBill Paul return (rval);
568a94100faSBill Paul }
569a94100faSBill Paul
570a94100faSBill Paul static int
re_miibus_writereg(device_t dev,int phy,int reg,int data)5717b5ffebfSPyun YongHyeon re_miibus_writereg(device_t dev, int phy, int reg, int data)
572a94100faSBill Paul {
573a94100faSBill Paul struct rl_softc *sc;
574a94100faSBill Paul u_int16_t re8139_reg = 0;
575a94100faSBill Paul int rval = 0;
576a94100faSBill Paul
577a94100faSBill Paul sc = device_get_softc(dev);
578a94100faSBill Paul
579a94100faSBill Paul if (sc->rl_type == RL_8169) {
580a94100faSBill Paul rval = re_gmii_writereg(dev, phy, reg, data);
581a94100faSBill Paul return (rval);
582a94100faSBill Paul }
583a94100faSBill Paul
584a94100faSBill Paul switch (reg) {
585a94100faSBill Paul case MII_BMCR:
586a94100faSBill Paul re8139_reg = RL_BMCR;
587baa12772SPyun YongHyeon if (sc->rl_type == RL_8139CPLUS) {
588baa12772SPyun YongHyeon /* 8139C+ has different bit layout. */
589baa12772SPyun YongHyeon data &= ~(BMCR_LOOP | BMCR_ISO);
590baa12772SPyun YongHyeon }
591a94100faSBill Paul break;
592a94100faSBill Paul case MII_BMSR:
593a94100faSBill Paul re8139_reg = RL_BMSR;
594a94100faSBill Paul break;
595a94100faSBill Paul case MII_ANAR:
596a94100faSBill Paul re8139_reg = RL_ANAR;
597a94100faSBill Paul break;
598a94100faSBill Paul case MII_ANER:
599a94100faSBill Paul re8139_reg = RL_ANER;
600a94100faSBill Paul break;
601a94100faSBill Paul case MII_ANLPAR:
602a94100faSBill Paul re8139_reg = RL_LPAR;
603a94100faSBill Paul break;
604a94100faSBill Paul case MII_PHYIDR1:
605a94100faSBill Paul case MII_PHYIDR2:
606a94100faSBill Paul return (0);
607a94100faSBill Paul break;
608a94100faSBill Paul default:
6096b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "bad phy register\n");
610a94100faSBill Paul return (0);
611a94100faSBill Paul }
612a94100faSBill Paul CSR_WRITE_2(sc, re8139_reg, data);
613a94100faSBill Paul return (0);
614a94100faSBill Paul }
615a94100faSBill Paul
616a94100faSBill Paul static void
re_miibus_statchg(device_t dev)6177b5ffebfSPyun YongHyeon re_miibus_statchg(device_t dev)
618a94100faSBill Paul {
619130b6dfbSPyun YongHyeon struct rl_softc *sc;
6204519a073SJustin Hibbits if_t ifp;
621130b6dfbSPyun YongHyeon struct mii_data *mii;
622a11e2f18SBruce M Simpson
623130b6dfbSPyun YongHyeon sc = device_get_softc(dev);
624130b6dfbSPyun YongHyeon mii = device_get_softc(sc->rl_miibus);
625130b6dfbSPyun YongHyeon ifp = sc->rl_ifp;
626130b6dfbSPyun YongHyeon if (mii == NULL || ifp == NULL ||
6274519a073SJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
628130b6dfbSPyun YongHyeon return;
629130b6dfbSPyun YongHyeon
630130b6dfbSPyun YongHyeon sc->rl_flags &= ~RL_FLAG_LINK;
631130b6dfbSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
632130b6dfbSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) {
633130b6dfbSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) {
634130b6dfbSPyun YongHyeon case IFM_10_T:
635130b6dfbSPyun YongHyeon case IFM_100_TX:
636130b6dfbSPyun YongHyeon sc->rl_flags |= RL_FLAG_LINK;
637130b6dfbSPyun YongHyeon break;
638130b6dfbSPyun YongHyeon case IFM_1000_T:
639130b6dfbSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0)
640130b6dfbSPyun YongHyeon break;
641130b6dfbSPyun YongHyeon sc->rl_flags |= RL_FLAG_LINK;
642130b6dfbSPyun YongHyeon break;
643130b6dfbSPyun YongHyeon default:
644130b6dfbSPyun YongHyeon break;
645130b6dfbSPyun YongHyeon }
646130b6dfbSPyun YongHyeon }
647130b6dfbSPyun YongHyeon /*
64814013280SMarius Strobl * RealTek controllers do not provide any interface to the RX/TX
64914013280SMarius Strobl * MACs for resolved speed, duplex and flow-control parameters.
650130b6dfbSPyun YongHyeon */
651a94100faSBill Paul }
652a94100faSBill Paul
653307b050dSGleb Smirnoff static u_int
re_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)654307b050dSGleb Smirnoff re_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
655307b050dSGleb Smirnoff {
656307b050dSGleb Smirnoff uint32_t h, *hashes = arg;
657307b050dSGleb Smirnoff
658307b050dSGleb Smirnoff h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 26;
659307b050dSGleb Smirnoff if (h < 32)
660307b050dSGleb Smirnoff hashes[0] |= (1 << h);
661307b050dSGleb Smirnoff else
662307b050dSGleb Smirnoff hashes[1] |= (1 << (h - 32));
663307b050dSGleb Smirnoff
664307b050dSGleb Smirnoff return (1);
665307b050dSGleb Smirnoff }
666307b050dSGleb Smirnoff
667a94100faSBill Paul /*
668ff191365SJung-uk Kim * Set the RX configuration and 64-bit multicast hash filter.
669a94100faSBill Paul */
670a94100faSBill Paul static void
re_set_rxmode(struct rl_softc * sc)671ff191365SJung-uk Kim re_set_rxmode(struct rl_softc *sc)
672a94100faSBill Paul {
6734519a073SJustin Hibbits if_t ifp;
674307b050dSGleb Smirnoff uint32_t h, hashes[2] = { 0, 0 };
675307b050dSGleb Smirnoff uint32_t rxfilt;
676a94100faSBill Paul
67797b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
67897b9d4baSJohn-Mark Gurney
679fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
680a94100faSBill Paul
681ff191365SJung-uk Kim rxfilt = RL_RXCFG_CONFIG | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_BROAD;
682f1a5f291SMarius Strobl if ((sc->rl_flags & RL_FLAG_EARLYOFF) != 0)
683f1a5f291SMarius Strobl rxfilt |= RL_RXCFG_EARLYOFF;
68414013280SMarius Strobl else if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0)
685f1a5f291SMarius Strobl rxfilt |= RL_RXCFG_EARLYOFFV2;
686a94100faSBill Paul
6874519a073SJustin Hibbits if (if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) {
6884519a073SJustin Hibbits if (if_getflags(ifp) & IFF_PROMISC)
6897c103000SPyun YongHyeon rxfilt |= RL_RXCFG_RX_ALLPHYS;
690a0637caaSPyun YongHyeon /*
691a0637caaSPyun YongHyeon * Unlike other hardwares, we have to explicitly set
692a0637caaSPyun YongHyeon * RL_RXCFG_RX_MULTI to receive multicast frames in
693a0637caaSPyun YongHyeon * promiscuous mode.
694a0637caaSPyun YongHyeon */
695a94100faSBill Paul rxfilt |= RL_RXCFG_RX_MULTI;
696ff191365SJung-uk Kim hashes[0] = hashes[1] = 0xffffffff;
697ff191365SJung-uk Kim goto done;
698a94100faSBill Paul }
699a94100faSBill Paul
700307b050dSGleb Smirnoff if_foreach_llmaddr(ifp, re_hash_maddr, hashes);
701a94100faSBill Paul
702ff191365SJung-uk Kim if (hashes[0] != 0 || hashes[1] != 0) {
703bb7dfefbSBill Paul /*
704ff191365SJung-uk Kim * For some unfathomable reason, RealTek decided to
705ff191365SJung-uk Kim * reverse the order of the multicast hash registers
706ff191365SJung-uk Kim * in the PCI Express parts. This means we have to
707ff191365SJung-uk Kim * write the hash pattern in reverse order for those
708ff191365SJung-uk Kim * devices.
709bb7dfefbSBill Paul */
710aaab4fbeSJung-uk Kim if ((sc->rl_flags & RL_FLAG_PCIE) != 0) {
711ff191365SJung-uk Kim h = bswap32(hashes[0]);
712ff191365SJung-uk Kim hashes[0] = bswap32(hashes[1]);
713ff191365SJung-uk Kim hashes[1] = h;
714ff191365SJung-uk Kim }
715ff191365SJung-uk Kim rxfilt |= RL_RXCFG_RX_MULTI;
716ff191365SJung-uk Kim }
717ff191365SJung-uk Kim
718b8333e45SPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8168F) {
719b8333e45SPyun YongHyeon /* Disable multicast filtering due to silicon bug. */
720b8333e45SPyun YongHyeon hashes[0] = 0xffffffff;
721b8333e45SPyun YongHyeon hashes[1] = 0xffffffff;
722b8333e45SPyun YongHyeon }
723b8333e45SPyun YongHyeon
724ff191365SJung-uk Kim done:
725a94100faSBill Paul CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
726a94100faSBill Paul CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
727ff191365SJung-uk Kim CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
728bb7dfefbSBill Paul }
729a94100faSBill Paul
730a94100faSBill Paul static void
re_reset(struct rl_softc * sc)7317b5ffebfSPyun YongHyeon re_reset(struct rl_softc *sc)
732a94100faSBill Paul {
7330ce0868aSPyun YongHyeon int i;
734a94100faSBill Paul
73597b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
73697b9d4baSJohn-Mark Gurney
737a94100faSBill Paul CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET);
738a94100faSBill Paul
739a94100faSBill Paul for (i = 0; i < RL_TIMEOUT; i++) {
740a94100faSBill Paul DELAY(10);
741a94100faSBill Paul if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET))
742a94100faSBill Paul break;
743a94100faSBill Paul }
744a94100faSBill Paul if (i == RL_TIMEOUT)
7456b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "reset never completed!\n");
746a94100faSBill Paul
747566ca8caSJung-uk Kim if ((sc->rl_flags & RL_FLAG_MACRESET) != 0)
748a94100faSBill Paul CSR_WRITE_1(sc, 0x82, 1);
74981eee0ebSPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8169S)
750566ca8caSJung-uk Kim re_gmii_writereg(sc->rl_dev, 1, 0x0b, 0);
751a94100faSBill Paul }
752a94100faSBill Paul
753ed510fb0SBill Paul #ifdef RE_DIAG
754ed510fb0SBill Paul
755a94100faSBill Paul /*
756a94100faSBill Paul * The following routine is designed to test for a defect on some
757a94100faSBill Paul * 32-bit 8169 cards. Some of these NICs have the REQ64# and ACK64#
758a94100faSBill Paul * lines connected to the bus, however for a 32-bit only card, they
759a94100faSBill Paul * should be pulled high. The result of this defect is that the
760a94100faSBill Paul * NIC will not work right if you plug it into a 64-bit slot: DMA
761a94100faSBill Paul * operations will be done with 64-bit transfers, which will fail
762a94100faSBill Paul * because the 64-bit data lines aren't connected.
763a94100faSBill Paul *
764a94100faSBill Paul * There's no way to work around this (short of talking a soldering
765a94100faSBill Paul * iron to the board), however we can detect it. The method we use
766a94100faSBill Paul * here is to put the NIC into digital loopback mode, set the receiver
767a94100faSBill Paul * to promiscuous mode, and then try to send a frame. We then compare
768a94100faSBill Paul * the frame data we sent to what was received. If the data matches,
769a94100faSBill Paul * then the NIC is working correctly, otherwise we know the user has
770a94100faSBill Paul * a defective NIC which has been mistakenly plugged into a 64-bit PCI
771a94100faSBill Paul * slot. In the latter case, there's no way the NIC can work correctly,
772a94100faSBill Paul * so we print out a message on the console and abort the device attach.
773a94100faSBill Paul */
774a94100faSBill Paul
775a94100faSBill Paul static int
re_diag(struct rl_softc * sc)7767b5ffebfSPyun YongHyeon re_diag(struct rl_softc *sc)
777a94100faSBill Paul {
7784519a073SJustin Hibbits if_t ifp = sc->rl_ifp;
779a94100faSBill Paul struct mbuf *m0;
780a94100faSBill Paul struct ether_header *eh;
781a94100faSBill Paul struct rl_desc *cur_rx;
782a94100faSBill Paul u_int16_t status;
783a94100faSBill Paul u_int32_t rxstat;
784ed510fb0SBill Paul int total_len, i, error = 0, phyaddr;
785a94100faSBill Paul u_int8_t dst[] = { 0x00, 'h', 'e', 'l', 'l', 'o' };
786a94100faSBill Paul u_int8_t src[] = { 0x00, 'w', 'o', 'r', 'l', 'd' };
787a94100faSBill Paul
788a94100faSBill Paul /* Allocate a single mbuf */
789c6499eccSGleb Smirnoff MGETHDR(m0, M_NOWAIT, MT_DATA);
790a94100faSBill Paul if (m0 == NULL)
791a94100faSBill Paul return (ENOBUFS);
792a94100faSBill Paul
79397b9d4baSJohn-Mark Gurney RL_LOCK(sc);
79497b9d4baSJohn-Mark Gurney
795a94100faSBill Paul /*
796a94100faSBill Paul * Initialize the NIC in test mode. This sets the chip up
797a94100faSBill Paul * so that it can send and receive frames, but performs the
798a94100faSBill Paul * following special functions:
799a94100faSBill Paul * - Puts receiver in promiscuous mode
800a94100faSBill Paul * - Enables digital loopback mode
801a94100faSBill Paul * - Leaves interrupts turned off
802a94100faSBill Paul */
803a94100faSBill Paul
8044519a073SJustin Hibbits if_setflagbit(ifp, IFF_PROMISC, 0);
805a94100faSBill Paul sc->rl_testmode = 1;
8064519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
80797b9d4baSJohn-Mark Gurney re_init_locked(sc);
808351a76f9SPyun YongHyeon sc->rl_flags |= RL_FLAG_LINK;
809ed510fb0SBill Paul if (sc->rl_type == RL_8169)
810ed510fb0SBill Paul phyaddr = 1;
811ed510fb0SBill Paul else
812ed510fb0SBill Paul phyaddr = 0;
813ed510fb0SBill Paul
814ed510fb0SBill Paul re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_RESET);
815ed510fb0SBill Paul for (i = 0; i < RL_TIMEOUT; i++) {
816ed510fb0SBill Paul status = re_miibus_readreg(sc->rl_dev, phyaddr, MII_BMCR);
817ed510fb0SBill Paul if (!(status & BMCR_RESET))
818ed510fb0SBill Paul break;
819ed510fb0SBill Paul }
820ed510fb0SBill Paul
821ed510fb0SBill Paul re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_LOOP);
822ed510fb0SBill Paul CSR_WRITE_2(sc, RL_ISR, RL_INTRS);
823ed510fb0SBill Paul
824804af9a1SBill Paul DELAY(100000);
825a94100faSBill Paul
826a94100faSBill Paul /* Put some data in the mbuf */
827a94100faSBill Paul
828a94100faSBill Paul eh = mtod(m0, struct ether_header *);
829a94100faSBill Paul bcopy ((char *)&dst, eh->ether_dhost, ETHER_ADDR_LEN);
830a94100faSBill Paul bcopy ((char *)&src, eh->ether_shost, ETHER_ADDR_LEN);
831a94100faSBill Paul eh->ether_type = htons(ETHERTYPE_IP);
832a94100faSBill Paul m0->m_pkthdr.len = m0->m_len = ETHER_MIN_LEN - ETHER_CRC_LEN;
833a94100faSBill Paul
8347cae6651SBill Paul /*
8357cae6651SBill Paul * Queue the packet, start transmission.
8367cae6651SBill Paul * Note: IF_HANDOFF() ultimately calls re_start() for us.
8377cae6651SBill Paul */
838a94100faSBill Paul
839abc8ff44SBill Paul CSR_WRITE_2(sc, RL_ISR, 0xFFFF);
84097b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
84152732175SMax Laier /* XXX: re_diag must not be called when in ALTQ mode */
8424519a073SJustin Hibbits if_handoff(ifp, m0, ifp);
84397b9d4baSJohn-Mark Gurney RL_LOCK(sc);
844a94100faSBill Paul m0 = NULL;
845a94100faSBill Paul
846a94100faSBill Paul /* Wait for it to propagate through the chip */
847a94100faSBill Paul
848abc8ff44SBill Paul DELAY(100000);
849a94100faSBill Paul for (i = 0; i < RL_TIMEOUT; i++) {
850a94100faSBill Paul status = CSR_READ_2(sc, RL_ISR);
851ed510fb0SBill Paul CSR_WRITE_2(sc, RL_ISR, status);
852abc8ff44SBill Paul if ((status & (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) ==
853abc8ff44SBill Paul (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK))
854a94100faSBill Paul break;
855a94100faSBill Paul DELAY(10);
856a94100faSBill Paul }
857a94100faSBill Paul
858a94100faSBill Paul if (i == RL_TIMEOUT) {
8596b9f5c94SGleb Smirnoff device_printf(sc->rl_dev,
8606b9f5c94SGleb Smirnoff "diagnostic failed, failed to receive packet in"
8616b9f5c94SGleb Smirnoff " loopback mode\n");
862a94100faSBill Paul error = EIO;
863a94100faSBill Paul goto done;
864a94100faSBill Paul }
865a94100faSBill Paul
866a94100faSBill Paul /*
867a94100faSBill Paul * The packet should have been dumped into the first
868a94100faSBill Paul * entry in the RX DMA ring. Grab it from there.
869a94100faSBill Paul */
870a94100faSBill Paul
871a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
872a94100faSBill Paul sc->rl_ldata.rl_rx_list_map,
873a94100faSBill Paul BUS_DMASYNC_POSTREAD);
874d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag,
875d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc[0].rx_dmamap,
876d65abd66SPyun YongHyeon BUS_DMASYNC_POSTREAD);
877d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag,
878d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc[0].rx_dmamap);
879a94100faSBill Paul
880d65abd66SPyun YongHyeon m0 = sc->rl_ldata.rl_rx_desc[0].rx_m;
881d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc[0].rx_m = NULL;
882a94100faSBill Paul eh = mtod(m0, struct ether_header *);
883a94100faSBill Paul
884a94100faSBill Paul cur_rx = &sc->rl_ldata.rl_rx_list[0];
885a94100faSBill Paul total_len = RL_RXBYTES(cur_rx);
886a94100faSBill Paul rxstat = le32toh(cur_rx->rl_cmdstat);
887a94100faSBill Paul
888a94100faSBill Paul if (total_len != ETHER_MIN_LEN) {
8896b9f5c94SGleb Smirnoff device_printf(sc->rl_dev,
8906b9f5c94SGleb Smirnoff "diagnostic failed, received short packet\n");
891a94100faSBill Paul error = EIO;
892a94100faSBill Paul goto done;
893a94100faSBill Paul }
894a94100faSBill Paul
895a94100faSBill Paul /* Test that the received packet data matches what we sent. */
896a94100faSBill Paul
897a94100faSBill Paul if (bcmp((char *)&eh->ether_dhost, (char *)&dst, ETHER_ADDR_LEN) ||
898a94100faSBill Paul bcmp((char *)&eh->ether_shost, (char *)&src, ETHER_ADDR_LEN) ||
899a94100faSBill Paul ntohs(eh->ether_type) != ETHERTYPE_IP) {
9006b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "WARNING, DMA FAILURE!\n");
9016b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "expected TX data: %6D/%6D/0x%x\n",
902a94100faSBill Paul dst, ":", src, ":", ETHERTYPE_IP);
9036b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "received RX data: %6D/%6D/0x%x\n",
904a94100faSBill Paul eh->ether_dhost, ":", eh->ether_shost, ":",
905a94100faSBill Paul ntohs(eh->ether_type));
9066b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "You may have a defective 32-bit "
9076b9f5c94SGleb Smirnoff "NIC plugged into a 64-bit PCI slot.\n");
9086b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "Please re-install the NIC in a "
9096b9f5c94SGleb Smirnoff "32-bit slot for proper operation.\n");
9106b9f5c94SGleb Smirnoff device_printf(sc->rl_dev, "Read the re(4) man page for more "
9116b9f5c94SGleb Smirnoff "details.\n");
912a94100faSBill Paul error = EIO;
913a94100faSBill Paul }
914a94100faSBill Paul
915a94100faSBill Paul done:
916a94100faSBill Paul /* Turn interface off, release resources */
917a94100faSBill Paul
918a94100faSBill Paul sc->rl_testmode = 0;
919351a76f9SPyun YongHyeon sc->rl_flags &= ~RL_FLAG_LINK;
9204519a073SJustin Hibbits if_setflagbit(ifp, 0, IFF_PROMISC);
921a94100faSBill Paul re_stop(sc);
922a94100faSBill Paul if (m0 != NULL)
923a94100faSBill Paul m_freem(m0);
924a94100faSBill Paul
92597b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
92697b9d4baSJohn-Mark Gurney
927a94100faSBill Paul return (error);
928a94100faSBill Paul }
929a94100faSBill Paul
930ed510fb0SBill Paul #endif
931ed510fb0SBill Paul
932a94100faSBill Paul /*
933a94100faSBill Paul * Probe for a RealTek 8139C+/8169/8110 chip. Check the PCI vendor and device
934a94100faSBill Paul * IDs against our list and return a device name if we find a match.
935a94100faSBill Paul */
936a94100faSBill Paul static int
re_probe(device_t dev)9377b5ffebfSPyun YongHyeon re_probe(device_t dev)
938a94100faSBill Paul {
939b3030306SMarius Strobl const struct rl_type *t;
940dfdb409eSPyun YongHyeon uint16_t devid, vendor;
941dfdb409eSPyun YongHyeon uint16_t revid, sdevid;
942dfdb409eSPyun YongHyeon int i;
943a94100faSBill Paul
944dfdb409eSPyun YongHyeon vendor = pci_get_vendor(dev);
945dfdb409eSPyun YongHyeon devid = pci_get_device(dev);
946dfdb409eSPyun YongHyeon revid = pci_get_revid(dev);
947dfdb409eSPyun YongHyeon sdevid = pci_get_subdevice(dev);
948a94100faSBill Paul
949dfdb409eSPyun YongHyeon if (vendor == LINKSYS_VENDORID && devid == LINKSYS_DEVICEID_EG1032) {
950dfdb409eSPyun YongHyeon if (sdevid != LINKSYS_SUBDEVICE_EG1032_REV3) {
95126390635SJohn Baldwin /*
95226390635SJohn Baldwin * Only attach to rev. 3 of the Linksys EG1032 adapter.
953dfdb409eSPyun YongHyeon * Rev. 2 is supported by sk(4).
95426390635SJohn Baldwin */
955a94100faSBill Paul return (ENXIO);
956a94100faSBill Paul }
957dfdb409eSPyun YongHyeon }
958dfdb409eSPyun YongHyeon
959dfdb409eSPyun YongHyeon if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) {
960dfdb409eSPyun YongHyeon if (revid != 0x20) {
961dfdb409eSPyun YongHyeon /* 8139, let rl(4) take care of this device. */
962dfdb409eSPyun YongHyeon return (ENXIO);
963dfdb409eSPyun YongHyeon }
964dfdb409eSPyun YongHyeon }
965dfdb409eSPyun YongHyeon
966dfdb409eSPyun YongHyeon t = re_devs;
96773a1170aSPedro F. Giffuni for (i = 0; i < nitems(re_devs); i++, t++) {
968dfdb409eSPyun YongHyeon if (vendor == t->rl_vid && devid == t->rl_did) {
969a94100faSBill Paul device_set_desc(dev, t->rl_name);
970d2b677bbSWarner Losh return (BUS_PROBE_DEFAULT);
971a94100faSBill Paul }
972a94100faSBill Paul }
973a94100faSBill Paul
974a94100faSBill Paul return (ENXIO);
975a94100faSBill Paul }
976a94100faSBill Paul
977a94100faSBill Paul /*
978a94100faSBill Paul * Map a single buffer address.
979a94100faSBill Paul */
980a94100faSBill Paul
981a94100faSBill Paul static void
re_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nseg,int error)9827b5ffebfSPyun YongHyeon re_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
983a94100faSBill Paul {
9848fd99e38SPyun YongHyeon bus_addr_t *addr;
985a94100faSBill Paul
986a94100faSBill Paul if (error)
987a94100faSBill Paul return;
988a94100faSBill Paul
989a94100faSBill Paul KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
990a94100faSBill Paul addr = arg;
991a94100faSBill Paul *addr = segs->ds_addr;
992a94100faSBill Paul }
993a94100faSBill Paul
994a94100faSBill Paul static int
re_allocmem(device_t dev,struct rl_softc * sc)9957b5ffebfSPyun YongHyeon re_allocmem(device_t dev, struct rl_softc *sc)
996a94100faSBill Paul {
99766366ca4SPyun YongHyeon bus_addr_t lowaddr;
998d65abd66SPyun YongHyeon bus_size_t rx_list_size, tx_list_size;
999a94100faSBill Paul int error;
1000a94100faSBill Paul int i;
1001a94100faSBill Paul
1002d65abd66SPyun YongHyeon rx_list_size = sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc);
1003d65abd66SPyun YongHyeon tx_list_size = sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc);
1004d65abd66SPyun YongHyeon
1005d65abd66SPyun YongHyeon /*
1006d65abd66SPyun YongHyeon * Allocate the parent bus DMA tag appropriate for PCI.
1007ce628393SPyun YongHyeon * In order to use DAC, RL_CPLUSCMD_PCI_DAC bit of RL_CPLUS_CMD
1008ce628393SPyun YongHyeon * register should be set. However some RealTek chips are known
1009ce628393SPyun YongHyeon * to be buggy on DAC handling, therefore disable DAC by limiting
1010ce628393SPyun YongHyeon * DMA address space to 32bit. PCIe variants of RealTek chips
101166366ca4SPyun YongHyeon * may not have the limitation.
1012d65abd66SPyun YongHyeon */
101366366ca4SPyun YongHyeon lowaddr = BUS_SPACE_MAXADDR;
101466366ca4SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_PCIE) == 0)
101566366ca4SPyun YongHyeon lowaddr = BUS_SPACE_MAXADDR_32BIT;
1016d65abd66SPyun YongHyeon error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
101766366ca4SPyun YongHyeon lowaddr, BUS_SPACE_MAXADDR, NULL, NULL,
1018d65abd66SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0,
1019d65abd66SPyun YongHyeon NULL, NULL, &sc->rl_parent_tag);
1020d65abd66SPyun YongHyeon if (error) {
1021d65abd66SPyun YongHyeon device_printf(dev, "could not allocate parent DMA tag\n");
1022d65abd66SPyun YongHyeon return (error);
1023d65abd66SPyun YongHyeon }
1024d65abd66SPyun YongHyeon
1025d65abd66SPyun YongHyeon /*
1026d65abd66SPyun YongHyeon * Allocate map for TX mbufs.
1027d65abd66SPyun YongHyeon */
1028d65abd66SPyun YongHyeon error = bus_dma_tag_create(sc->rl_parent_tag, 1, 0,
1029d65abd66SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL,
1030d65abd66SPyun YongHyeon NULL, MCLBYTES * RL_NTXSEGS, RL_NTXSEGS, 4096, 0,
1031d65abd66SPyun YongHyeon NULL, NULL, &sc->rl_ldata.rl_tx_mtag);
1032d65abd66SPyun YongHyeon if (error) {
1033d65abd66SPyun YongHyeon device_printf(dev, "could not allocate TX DMA tag\n");
1034d65abd66SPyun YongHyeon return (error);
1035d65abd66SPyun YongHyeon }
1036d65abd66SPyun YongHyeon
1037a94100faSBill Paul /*
1038a94100faSBill Paul * Allocate map for RX mbufs.
1039a94100faSBill Paul */
1040d65abd66SPyun YongHyeon
104181eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
104281eee0ebSPyun YongHyeon error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t),
104381eee0ebSPyun YongHyeon 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
104481eee0ebSPyun YongHyeon MJUM9BYTES, 1, MJUM9BYTES, 0, NULL, NULL,
104581eee0ebSPyun YongHyeon &sc->rl_ldata.rl_jrx_mtag);
104681eee0ebSPyun YongHyeon if (error) {
104781eee0ebSPyun YongHyeon device_printf(dev,
104881eee0ebSPyun YongHyeon "could not allocate jumbo RX DMA tag\n");
104981eee0ebSPyun YongHyeon return (error);
105081eee0ebSPyun YongHyeon }
105181eee0ebSPyun YongHyeon }
1052d65abd66SPyun YongHyeon error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t), 0,
1053d65abd66SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
1054d65abd66SPyun YongHyeon MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->rl_ldata.rl_rx_mtag);
1055a94100faSBill Paul if (error) {
1056d65abd66SPyun YongHyeon device_printf(dev, "could not allocate RX DMA tag\n");
1057d65abd66SPyun YongHyeon return (error);
1058a94100faSBill Paul }
1059a94100faSBill Paul
1060a94100faSBill Paul /*
1061a94100faSBill Paul * Allocate map for TX descriptor list.
1062a94100faSBill Paul */
1063a94100faSBill Paul error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN,
1064a94100faSBill Paul 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
1065d65abd66SPyun YongHyeon NULL, tx_list_size, 1, tx_list_size, 0,
1066a94100faSBill Paul NULL, NULL, &sc->rl_ldata.rl_tx_list_tag);
1067a94100faSBill Paul if (error) {
1068d65abd66SPyun YongHyeon device_printf(dev, "could not allocate TX DMA ring tag\n");
1069d65abd66SPyun YongHyeon return (error);
1070a94100faSBill Paul }
1071a94100faSBill Paul
1072a94100faSBill Paul /* Allocate DMA'able memory for the TX ring */
1073a94100faSBill Paul
1074a94100faSBill Paul error = bus_dmamem_alloc(sc->rl_ldata.rl_tx_list_tag,
1075d65abd66SPyun YongHyeon (void **)&sc->rl_ldata.rl_tx_list,
1076d65abd66SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
1077a94100faSBill Paul &sc->rl_ldata.rl_tx_list_map);
1078d65abd66SPyun YongHyeon if (error) {
1079d65abd66SPyun YongHyeon device_printf(dev, "could not allocate TX DMA ring\n");
1080d65abd66SPyun YongHyeon return (error);
1081d65abd66SPyun YongHyeon }
1082a94100faSBill Paul
1083a94100faSBill Paul /* Load the map for the TX ring. */
1084a94100faSBill Paul
1085d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_list_addr = 0;
1086a94100faSBill Paul error = bus_dmamap_load(sc->rl_ldata.rl_tx_list_tag,
1087a94100faSBill Paul sc->rl_ldata.rl_tx_list_map, sc->rl_ldata.rl_tx_list,
1088d65abd66SPyun YongHyeon tx_list_size, re_dma_map_addr,
1089a94100faSBill Paul &sc->rl_ldata.rl_tx_list_addr, BUS_DMA_NOWAIT);
1090d65abd66SPyun YongHyeon if (error != 0 || sc->rl_ldata.rl_tx_list_addr == 0) {
1091d65abd66SPyun YongHyeon device_printf(dev, "could not load TX DMA ring\n");
1092d65abd66SPyun YongHyeon return (ENOMEM);
1093d65abd66SPyun YongHyeon }
1094a94100faSBill Paul
1095a94100faSBill Paul /* Create DMA maps for TX buffers */
1096a94100faSBill Paul
1097d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
1098d65abd66SPyun YongHyeon error = bus_dmamap_create(sc->rl_ldata.rl_tx_mtag, 0,
1099d65abd66SPyun YongHyeon &sc->rl_ldata.rl_tx_desc[i].tx_dmamap);
1100a94100faSBill Paul if (error) {
1101d65abd66SPyun YongHyeon device_printf(dev, "could not create DMA map for TX\n");
1102d65abd66SPyun YongHyeon return (error);
1103a94100faSBill Paul }
1104a94100faSBill Paul }
1105a94100faSBill Paul
1106a94100faSBill Paul /*
1107a94100faSBill Paul * Allocate map for RX descriptor list.
1108a94100faSBill Paul */
1109a94100faSBill Paul error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN,
1110a94100faSBill Paul 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
1111d65abd66SPyun YongHyeon NULL, rx_list_size, 1, rx_list_size, 0,
1112a94100faSBill Paul NULL, NULL, &sc->rl_ldata.rl_rx_list_tag);
1113a94100faSBill Paul if (error) {
1114d65abd66SPyun YongHyeon device_printf(dev, "could not create RX DMA ring tag\n");
1115d65abd66SPyun YongHyeon return (error);
1116a94100faSBill Paul }
1117a94100faSBill Paul
1118a94100faSBill Paul /* Allocate DMA'able memory for the RX ring */
1119a94100faSBill Paul
1120a94100faSBill Paul error = bus_dmamem_alloc(sc->rl_ldata.rl_rx_list_tag,
1121d65abd66SPyun YongHyeon (void **)&sc->rl_ldata.rl_rx_list,
1122d65abd66SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
1123a94100faSBill Paul &sc->rl_ldata.rl_rx_list_map);
1124d65abd66SPyun YongHyeon if (error) {
1125d65abd66SPyun YongHyeon device_printf(dev, "could not allocate RX DMA ring\n");
1126d65abd66SPyun YongHyeon return (error);
1127d65abd66SPyun YongHyeon }
1128a94100faSBill Paul
1129a94100faSBill Paul /* Load the map for the RX ring. */
1130a94100faSBill Paul
1131d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_list_addr = 0;
1132a94100faSBill Paul error = bus_dmamap_load(sc->rl_ldata.rl_rx_list_tag,
1133a94100faSBill Paul sc->rl_ldata.rl_rx_list_map, sc->rl_ldata.rl_rx_list,
1134d65abd66SPyun YongHyeon rx_list_size, re_dma_map_addr,
1135a94100faSBill Paul &sc->rl_ldata.rl_rx_list_addr, BUS_DMA_NOWAIT);
1136d65abd66SPyun YongHyeon if (error != 0 || sc->rl_ldata.rl_rx_list_addr == 0) {
1137d65abd66SPyun YongHyeon device_printf(dev, "could not load RX DMA ring\n");
1138d65abd66SPyun YongHyeon return (ENOMEM);
1139d65abd66SPyun YongHyeon }
1140a94100faSBill Paul
1141a94100faSBill Paul /* Create DMA maps for RX buffers */
1142a94100faSBill Paul
114381eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
114481eee0ebSPyun YongHyeon error = bus_dmamap_create(sc->rl_ldata.rl_jrx_mtag, 0,
114581eee0ebSPyun YongHyeon &sc->rl_ldata.rl_jrx_sparemap);
114681eee0ebSPyun YongHyeon if (error) {
114781eee0ebSPyun YongHyeon device_printf(dev,
114881eee0ebSPyun YongHyeon "could not create spare DMA map for jumbo RX\n");
114981eee0ebSPyun YongHyeon return (error);
115081eee0ebSPyun YongHyeon }
115181eee0ebSPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
115281eee0ebSPyun YongHyeon error = bus_dmamap_create(sc->rl_ldata.rl_jrx_mtag, 0,
115381eee0ebSPyun YongHyeon &sc->rl_ldata.rl_jrx_desc[i].rx_dmamap);
115481eee0ebSPyun YongHyeon if (error) {
115581eee0ebSPyun YongHyeon device_printf(dev,
115681eee0ebSPyun YongHyeon "could not create DMA map for jumbo RX\n");
115781eee0ebSPyun YongHyeon return (error);
115881eee0ebSPyun YongHyeon }
115981eee0ebSPyun YongHyeon }
116081eee0ebSPyun YongHyeon }
1161d65abd66SPyun YongHyeon error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0,
1162d65abd66SPyun YongHyeon &sc->rl_ldata.rl_rx_sparemap);
1163a94100faSBill Paul if (error) {
1164d65abd66SPyun YongHyeon device_printf(dev, "could not create spare DMA map for RX\n");
1165d65abd66SPyun YongHyeon return (error);
1166d65abd66SPyun YongHyeon }
1167d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
1168d65abd66SPyun YongHyeon error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0,
1169d65abd66SPyun YongHyeon &sc->rl_ldata.rl_rx_desc[i].rx_dmamap);
1170d65abd66SPyun YongHyeon if (error) {
1171d65abd66SPyun YongHyeon device_printf(dev, "could not create DMA map for RX\n");
1172d65abd66SPyun YongHyeon return (error);
1173a94100faSBill Paul }
1174a94100faSBill Paul }
1175a94100faSBill Paul
11760534aae0SPyun YongHyeon /* Create DMA map for statistics. */
11770534aae0SPyun YongHyeon error = bus_dma_tag_create(sc->rl_parent_tag, RL_DUMP_ALIGN, 0,
11780534aae0SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
11790534aae0SPyun YongHyeon sizeof(struct rl_stats), 1, sizeof(struct rl_stats), 0, NULL, NULL,
11800534aae0SPyun YongHyeon &sc->rl_ldata.rl_stag);
11810534aae0SPyun YongHyeon if (error) {
11820534aae0SPyun YongHyeon device_printf(dev, "could not create statistics DMA tag\n");
11830534aae0SPyun YongHyeon return (error);
11840534aae0SPyun YongHyeon }
11850534aae0SPyun YongHyeon /* Allocate DMA'able memory for statistics. */
11860534aae0SPyun YongHyeon error = bus_dmamem_alloc(sc->rl_ldata.rl_stag,
11870534aae0SPyun YongHyeon (void **)&sc->rl_ldata.rl_stats,
11880534aae0SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
11890534aae0SPyun YongHyeon &sc->rl_ldata.rl_smap);
11900534aae0SPyun YongHyeon if (error) {
11910534aae0SPyun YongHyeon device_printf(dev,
11920534aae0SPyun YongHyeon "could not allocate statistics DMA memory\n");
11930534aae0SPyun YongHyeon return (error);
11940534aae0SPyun YongHyeon }
11950534aae0SPyun YongHyeon /* Load the map for statistics. */
11960534aae0SPyun YongHyeon sc->rl_ldata.rl_stats_addr = 0;
11970534aae0SPyun YongHyeon error = bus_dmamap_load(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_smap,
11980534aae0SPyun YongHyeon sc->rl_ldata.rl_stats, sizeof(struct rl_stats), re_dma_map_addr,
11990534aae0SPyun YongHyeon &sc->rl_ldata.rl_stats_addr, BUS_DMA_NOWAIT);
12000534aae0SPyun YongHyeon if (error != 0 || sc->rl_ldata.rl_stats_addr == 0) {
12010534aae0SPyun YongHyeon device_printf(dev, "could not load statistics DMA memory\n");
12020534aae0SPyun YongHyeon return (ENOMEM);
12030534aae0SPyun YongHyeon }
12040534aae0SPyun YongHyeon
1205a94100faSBill Paul return (0);
1206a94100faSBill Paul }
1207a94100faSBill Paul
1208a94100faSBill Paul /*
1209a94100faSBill Paul * Attach the interface. Allocate softc structures, do ifmedia
1210a94100faSBill Paul * setup and ethernet/BPF attach.
1211a94100faSBill Paul */
1212a94100faSBill Paul static int
re_attach(device_t dev)12137b5ffebfSPyun YongHyeon re_attach(device_t dev)
1214a94100faSBill Paul {
1215a94100faSBill Paul u_char eaddr[ETHER_ADDR_LEN];
1216be099007SPyun YongHyeon u_int16_t as[ETHER_ADDR_LEN / 2];
1217a94100faSBill Paul struct rl_softc *sc;
12184519a073SJustin Hibbits if_t ifp;
1219b3030306SMarius Strobl const struct rl_hwrev *hw_rev;
122014013280SMarius Strobl int capmask, error = 0, hwrev, i, msic, msixc,
122114013280SMarius Strobl phy, reg, rid;
1222017f1c8dSPyun YongHyeon u_int32_t cap, ctl;
1223ace7ed5dSPyun YongHyeon u_int16_t devid, re_did = 0;
122403ca7ae8SPyun YongHyeon uint8_t cfg;
1225a94100faSBill Paul
1226a94100faSBill Paul sc = device_get_softc(dev);
1227ed510fb0SBill Paul sc->rl_dev = dev;
1228a94100faSBill Paul
1229a94100faSBill Paul mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
123097b9d4baSJohn-Mark Gurney MTX_DEF);
1231d1754a9bSJohn Baldwin callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0);
1232d1754a9bSJohn Baldwin
1233a94100faSBill Paul /*
1234a94100faSBill Paul * Map control/status registers.
1235a94100faSBill Paul */
1236a94100faSBill Paul pci_enable_busmaster(dev);
1237a94100faSBill Paul
1238ace7ed5dSPyun YongHyeon devid = pci_get_device(dev);
12392c21710bSPyun YongHyeon /*
12402c21710bSPyun YongHyeon * Prefer memory space register mapping over IO space.
12412c21710bSPyun YongHyeon * Because RTL8169SC does not seem to work when memory mapping
12422c21710bSPyun YongHyeon * is used always activate io mapping.
12432c21710bSPyun YongHyeon */
12442c21710bSPyun YongHyeon if (devid == RT_DEVICEID_8169SC)
12452c21710bSPyun YongHyeon prefer_iomap = 1;
12462c21710bSPyun YongHyeon if (prefer_iomap == 0) {
1247ace7ed5dSPyun YongHyeon sc->rl_res_id = PCIR_BAR(1);
1248ace7ed5dSPyun YongHyeon sc->rl_res_type = SYS_RES_MEMORY;
1249ace7ed5dSPyun YongHyeon /* RTL8168/8101E seems to use different BARs. */
1250ace7ed5dSPyun YongHyeon if (devid == RT_DEVICEID_8168 || devid == RT_DEVICEID_8101E)
1251ace7ed5dSPyun YongHyeon sc->rl_res_id = PCIR_BAR(2);
12522c21710bSPyun YongHyeon } else {
12532c21710bSPyun YongHyeon sc->rl_res_id = PCIR_BAR(0);
12542c21710bSPyun YongHyeon sc->rl_res_type = SYS_RES_IOPORT;
12552c21710bSPyun YongHyeon }
1256ace7ed5dSPyun YongHyeon sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type,
1257ace7ed5dSPyun YongHyeon &sc->rl_res_id, RF_ACTIVE);
12582c21710bSPyun YongHyeon if (sc->rl_res == NULL && prefer_iomap == 0) {
1259ace7ed5dSPyun YongHyeon sc->rl_res_id = PCIR_BAR(0);
1260ace7ed5dSPyun YongHyeon sc->rl_res_type = SYS_RES_IOPORT;
1261ace7ed5dSPyun YongHyeon sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type,
1262ace7ed5dSPyun YongHyeon &sc->rl_res_id, RF_ACTIVE);
12632c21710bSPyun YongHyeon }
1264ace7ed5dSPyun YongHyeon if (sc->rl_res == NULL) {
1265d1754a9bSJohn Baldwin device_printf(dev, "couldn't map ports/memory\n");
1266a94100faSBill Paul error = ENXIO;
1267a94100faSBill Paul goto fail;
1268a94100faSBill Paul }
1269a94100faSBill Paul
1270a94100faSBill Paul sc->rl_btag = rman_get_bustag(sc->rl_res);
1271a94100faSBill Paul sc->rl_bhandle = rman_get_bushandle(sc->rl_res);
1272a94100faSBill Paul
12735774c5ffSPyun YongHyeon msic = pci_msi_count(dev);
12744a58fd45SPyun YongHyeon msixc = pci_msix_count(dev);
1275017f1c8dSPyun YongHyeon if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) {
12764a58fd45SPyun YongHyeon sc->rl_flags |= RL_FLAG_PCIE;
1277017f1c8dSPyun YongHyeon sc->rl_expcap = reg;
1278017f1c8dSPyun YongHyeon }
12794a58fd45SPyun YongHyeon if (bootverbose) {
12805774c5ffSPyun YongHyeon device_printf(dev, "MSI count : %d\n", msic);
12814a58fd45SPyun YongHyeon device_printf(dev, "MSI-X count : %d\n", msixc);
12825774c5ffSPyun YongHyeon }
12834a58fd45SPyun YongHyeon if (msix_disable > 0)
12844a58fd45SPyun YongHyeon msixc = 0;
12854a58fd45SPyun YongHyeon if (msi_disable > 0)
12864a58fd45SPyun YongHyeon msic = 0;
12874a58fd45SPyun YongHyeon /* Prefer MSI-X to MSI. */
12884a58fd45SPyun YongHyeon if (msixc > 0) {
1289f1a5f291SMarius Strobl msixc = RL_MSI_MESSAGES;
12904a58fd45SPyun YongHyeon rid = PCIR_BAR(4);
12914a58fd45SPyun YongHyeon sc->rl_res_pba = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
12924a58fd45SPyun YongHyeon &rid, RF_ACTIVE);
12934a58fd45SPyun YongHyeon if (sc->rl_res_pba == NULL) {
12944a58fd45SPyun YongHyeon device_printf(sc->rl_dev,
12954a58fd45SPyun YongHyeon "could not allocate MSI-X PBA resource\n");
12964a58fd45SPyun YongHyeon }
12974a58fd45SPyun YongHyeon if (sc->rl_res_pba != NULL &&
12984a58fd45SPyun YongHyeon pci_alloc_msix(dev, &msixc) == 0) {
1299f1a5f291SMarius Strobl if (msixc == RL_MSI_MESSAGES) {
13004a58fd45SPyun YongHyeon device_printf(dev, "Using %d MSI-X message\n",
13014a58fd45SPyun YongHyeon msixc);
13024a58fd45SPyun YongHyeon sc->rl_flags |= RL_FLAG_MSIX;
13034a58fd45SPyun YongHyeon } else
13044a58fd45SPyun YongHyeon pci_release_msi(dev);
13054a58fd45SPyun YongHyeon }
13064a58fd45SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MSIX) == 0) {
13074a58fd45SPyun YongHyeon if (sc->rl_res_pba != NULL)
13084a58fd45SPyun YongHyeon bus_release_resource(dev, SYS_RES_MEMORY, rid,
13094a58fd45SPyun YongHyeon sc->rl_res_pba);
13104a58fd45SPyun YongHyeon sc->rl_res_pba = NULL;
13114a58fd45SPyun YongHyeon msixc = 0;
13124a58fd45SPyun YongHyeon }
13134a58fd45SPyun YongHyeon }
13144a58fd45SPyun YongHyeon /* Prefer MSI to INTx. */
13154a58fd45SPyun YongHyeon if (msixc == 0 && msic > 0) {
1316f1a5f291SMarius Strobl msic = RL_MSI_MESSAGES;
13175774c5ffSPyun YongHyeon if (pci_alloc_msi(dev, &msic) == 0) {
13185774c5ffSPyun YongHyeon if (msic == RL_MSI_MESSAGES) {
13194a58fd45SPyun YongHyeon device_printf(dev, "Using %d MSI message\n",
13205774c5ffSPyun YongHyeon msic);
1321351a76f9SPyun YongHyeon sc->rl_flags |= RL_FLAG_MSI;
1322339a44fbSPyun YongHyeon /* Explicitly set MSI enable bit. */
1323339a44fbSPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
1324339a44fbSPyun YongHyeon cfg = CSR_READ_1(sc, RL_CFG2);
1325339a44fbSPyun YongHyeon cfg |= RL_CFG2_MSI;
1326339a44fbSPyun YongHyeon CSR_WRITE_1(sc, RL_CFG2, cfg);
1327f98dd8cfSPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
13285774c5ffSPyun YongHyeon } else
13295774c5ffSPyun YongHyeon pci_release_msi(dev);
13305774c5ffSPyun YongHyeon }
13314a58fd45SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MSI) == 0)
13324a58fd45SPyun YongHyeon msic = 0;
13335774c5ffSPyun YongHyeon }
1334a94100faSBill Paul
13355774c5ffSPyun YongHyeon /* Allocate interrupt */
13364a58fd45SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) {
13375774c5ffSPyun YongHyeon rid = 0;
13385774c5ffSPyun YongHyeon sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
13395774c5ffSPyun YongHyeon RF_SHAREABLE | RF_ACTIVE);
13405774c5ffSPyun YongHyeon if (sc->rl_irq[0] == NULL) {
13415774c5ffSPyun YongHyeon device_printf(dev, "couldn't allocate IRQ resources\n");
1342a94100faSBill Paul error = ENXIO;
1343a94100faSBill Paul goto fail;
1344a94100faSBill Paul }
13455774c5ffSPyun YongHyeon } else {
13465774c5ffSPyun YongHyeon for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) {
13475774c5ffSPyun YongHyeon sc->rl_irq[i] = bus_alloc_resource_any(dev,
13485774c5ffSPyun YongHyeon SYS_RES_IRQ, &rid, RF_ACTIVE);
13495774c5ffSPyun YongHyeon if (sc->rl_irq[i] == NULL) {
13505774c5ffSPyun YongHyeon device_printf(dev,
13512df05392SSergey Kandaurov "couldn't allocate IRQ resources for "
13525774c5ffSPyun YongHyeon "message %d\n", rid);
13535774c5ffSPyun YongHyeon error = ENXIO;
13545774c5ffSPyun YongHyeon goto fail;
13555774c5ffSPyun YongHyeon }
13565774c5ffSPyun YongHyeon }
13575774c5ffSPyun YongHyeon }
1358a94100faSBill Paul
13594d2bf239SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MSI) == 0) {
13604d2bf239SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
13614d2bf239SPyun YongHyeon cfg = CSR_READ_1(sc, RL_CFG2);
13624d2bf239SPyun YongHyeon if ((cfg & RL_CFG2_MSI) != 0) {
13634d2bf239SPyun YongHyeon device_printf(dev, "turning off MSI enable bit.\n");
13644d2bf239SPyun YongHyeon cfg &= ~RL_CFG2_MSI;
13654d2bf239SPyun YongHyeon CSR_WRITE_1(sc, RL_CFG2, cfg);
13664d2bf239SPyun YongHyeon }
13674d2bf239SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
13684d2bf239SPyun YongHyeon }
13694d2bf239SPyun YongHyeon
13703d810282SKevin Lo /* Disable ASPM L0S/L1 and CLKREQ. */
1371017f1c8dSPyun YongHyeon if (sc->rl_expcap != 0) {
1372017f1c8dSPyun YongHyeon cap = pci_read_config(dev, sc->rl_expcap +
1373389c8bd5SGavin Atkinson PCIER_LINK_CAP, 2);
1374389c8bd5SGavin Atkinson if ((cap & PCIEM_LINK_CAP_ASPM) != 0) {
1375017f1c8dSPyun YongHyeon ctl = pci_read_config(dev, sc->rl_expcap +
1376389c8bd5SGavin Atkinson PCIER_LINK_CTL, 2);
13773d810282SKevin Lo if ((ctl & (PCIEM_LINK_CTL_ECPM |
13783d810282SKevin Lo PCIEM_LINK_CTL_ASPMC))!= 0) {
13793d810282SKevin Lo ctl &= ~(PCIEM_LINK_CTL_ECPM |
13803d810282SKevin Lo PCIEM_LINK_CTL_ASPMC);
1381017f1c8dSPyun YongHyeon pci_write_config(dev, sc->rl_expcap +
1382389c8bd5SGavin Atkinson PCIER_LINK_CTL, ctl, 2);
1383017f1c8dSPyun YongHyeon device_printf(dev, "ASPM disabled\n");
1384017f1c8dSPyun YongHyeon }
1385017f1c8dSPyun YongHyeon } else
1386017f1c8dSPyun YongHyeon device_printf(dev, "no ASPM capability\n");
1387017f1c8dSPyun YongHyeon }
1388017f1c8dSPyun YongHyeon
1389abc8ff44SBill Paul hw_rev = re_hwrevs;
1390a810fc83SPyun YongHyeon hwrev = CSR_READ_4(sc, RL_TXCFG);
1391566ca8caSJung-uk Kim switch (hwrev & 0x70000000) {
1392566ca8caSJung-uk Kim case 0x00000000:
1393566ca8caSJung-uk Kim case 0x10000000:
1394566ca8caSJung-uk Kim device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0xfc800000);
1395566ca8caSJung-uk Kim hwrev &= (RL_TXCFG_HWREV | 0x80000000);
1396566ca8caSJung-uk Kim break;
1397566ca8caSJung-uk Kim default:
1398a810fc83SPyun YongHyeon device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0x7c800000);
1399fd3ae0f5SPyun YongHyeon sc->rl_macrev = hwrev & 0x00700000;
1400a810fc83SPyun YongHyeon hwrev &= RL_TXCFG_HWREV;
1401566ca8caSJung-uk Kim break;
1402566ca8caSJung-uk Kim }
1403fd3ae0f5SPyun YongHyeon device_printf(dev, "MAC rev. 0x%08x\n", sc->rl_macrev);
1404abc8ff44SBill Paul while (hw_rev->rl_desc != NULL) {
1405abc8ff44SBill Paul if (hw_rev->rl_rev == hwrev) {
1406abc8ff44SBill Paul sc->rl_type = hw_rev->rl_type;
140781eee0ebSPyun YongHyeon sc->rl_hwrev = hw_rev;
1408abc8ff44SBill Paul break;
1409abc8ff44SBill Paul }
1410abc8ff44SBill Paul hw_rev++;
1411abc8ff44SBill Paul }
1412d65abd66SPyun YongHyeon if (hw_rev->rl_desc == NULL) {
1413a810fc83SPyun YongHyeon device_printf(dev, "Unknown H/W revision: 0x%08x\n", hwrev);
1414d65abd66SPyun YongHyeon error = ENXIO;
1415d65abd66SPyun YongHyeon goto fail;
1416d65abd66SPyun YongHyeon }
1417abc8ff44SBill Paul
1418351a76f9SPyun YongHyeon switch (hw_rev->rl_rev) {
1419351a76f9SPyun YongHyeon case RL_HWREV_8139CPLUS:
142081eee0ebSPyun YongHyeon sc->rl_flags |= RL_FLAG_FASTETHER | RL_FLAG_AUTOPAD;
1421351a76f9SPyun YongHyeon break;
1422351a76f9SPyun YongHyeon case RL_HWREV_8100E:
1423351a76f9SPyun YongHyeon case RL_HWREV_8101E:
142481eee0ebSPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_FASTETHER;
1425351a76f9SPyun YongHyeon break;
1426b1d62f0fSPyun YongHyeon case RL_HWREV_8102E:
1427b1d62f0fSPyun YongHyeon case RL_HWREV_8102EL:
14283d22427cSTai-hwa Liang case RL_HWREV_8102EL_SPIN1:
142981eee0ebSPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 |
143081eee0ebSPyun YongHyeon RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP |
143181eee0ebSPyun YongHyeon RL_FLAG_AUTOPAD;
1432b1d62f0fSPyun YongHyeon break;
14338281a098SPyun YongHyeon case RL_HWREV_8103E:
143481eee0ebSPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 |
143581eee0ebSPyun YongHyeon RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP |
143681eee0ebSPyun YongHyeon RL_FLAG_AUTOPAD | RL_FLAG_MACSLEEP;
14378281a098SPyun YongHyeon break;
143839e69201SPyun YongHyeon case RL_HWREV_8401E:
143954899a96SPyun YongHyeon case RL_HWREV_8105E:
14406b0a8e04SPyun YongHyeon case RL_HWREV_8105E_SPIN1:
1441214c71f6SPyun YongHyeon case RL_HWREV_8106E:
144254899a96SPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
144354899a96SPyun YongHyeon RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
144454899a96SPyun YongHyeon RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD;
144554899a96SPyun YongHyeon break;
1446eef0e496SPyun YongHyeon case RL_HWREV_8402:
1447eef0e496SPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
1448eef0e496SPyun YongHyeon RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
1449eef0e496SPyun YongHyeon RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD |
1450eef0e496SPyun YongHyeon RL_FLAG_CMDSTOP_WAIT_TXQ;
1451eef0e496SPyun YongHyeon break;
1452ef278cb4SPyun YongHyeon case RL_HWREV_8168B_SPIN1:
1453ef278cb4SPyun YongHyeon case RL_HWREV_8168B_SPIN2:
1454886ff602SPyun YongHyeon sc->rl_flags |= RL_FLAG_WOLRXENB;
1455886ff602SPyun YongHyeon /* FALLTHROUGH */
1456ef278cb4SPyun YongHyeon case RL_HWREV_8168B_SPIN3:
1457aaab4fbeSJung-uk Kim sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_MACSTAT;
1458deb5c680SPyun YongHyeon break;
1459deb5c680SPyun YongHyeon case RL_HWREV_8168C_SPIN2:
146061f45a72SPyun YongHyeon sc->rl_flags |= RL_FLAG_MACSLEEP;
146161f45a72SPyun YongHyeon /* FALLTHROUGH */
146261f45a72SPyun YongHyeon case RL_HWREV_8168C:
1463fd3ae0f5SPyun YongHyeon if (sc->rl_macrev == 0x00200000)
146461f45a72SPyun YongHyeon sc->rl_flags |= RL_FLAG_MACSLEEP;
146561f45a72SPyun YongHyeon /* FALLTHROUGH */
1466deb5c680SPyun YongHyeon case RL_HWREV_8168CP:
1467aaab4fbeSJung-uk Kim sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
1468f2e491c9SPyun YongHyeon RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
14696830588dSPyun YongHyeon RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 | RL_FLAG_WOL_MANLINK;
1470351a76f9SPyun YongHyeon break;
1471df2dc2b3SPyun YongHyeon case RL_HWREV_8168D:
1472df2dc2b3SPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
1473df2dc2b3SPyun YongHyeon RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
1474df2dc2b3SPyun YongHyeon RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
1475df2dc2b3SPyun YongHyeon RL_FLAG_WOL_MANLINK;
1476df2dc2b3SPyun YongHyeon break;
1477eef0e496SPyun YongHyeon case RL_HWREV_8168DP:
1478eef0e496SPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
1479eef0e496SPyun YongHyeon RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_AUTOPAD |
14806830588dSPyun YongHyeon RL_FLAG_JUMBOV2 | RL_FLAG_WAIT_TXPOLL | RL_FLAG_WOL_MANLINK;
1481eef0e496SPyun YongHyeon break;
1482d0c45156SPyun YongHyeon case RL_HWREV_8168E:
1483d0c45156SPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHYWAKE_PM |
1484d0c45156SPyun YongHyeon RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT |
14856830588dSPyun YongHyeon RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
14866830588dSPyun YongHyeon RL_FLAG_WOL_MANLINK;
1487d0c45156SPyun YongHyeon break;
1488f0431c5bSPyun YongHyeon case RL_HWREV_8168E_VL:
1489d467ffaaSPyun YongHyeon case RL_HWREV_8168F:
1490f1a5f291SMarius Strobl sc->rl_flags |= RL_FLAG_EARLYOFF;
1491f1a5f291SMarius Strobl /* FALLTHROUGH */
1492d56f7f52SPyun YongHyeon case RL_HWREV_8411:
1493f0431c5bSPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
1494f0431c5bSPyun YongHyeon RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
1495eef0e496SPyun YongHyeon RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
14966830588dSPyun YongHyeon RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK;
1497f0431c5bSPyun YongHyeon break;
1498f1a5f291SMarius Strobl case RL_HWREV_8168EP:
149988d2b69cSBrad Smith case RL_HWREV_8168FP:
1500f1a5f291SMarius Strobl case RL_HWREV_8168G:
1501f1a5f291SMarius Strobl case RL_HWREV_8411B:
1502f1a5f291SMarius Strobl sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
1503f1a5f291SMarius Strobl RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
1504f1a5f291SMarius Strobl RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 |
1505f1a5f291SMarius Strobl RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK |
150614013280SMarius Strobl RL_FLAG_8168G_PLUS;
1507f1a5f291SMarius Strobl break;
1508ab9f923eSPyun YongHyeon case RL_HWREV_8168GU:
150914013280SMarius Strobl case RL_HWREV_8168H:
1510ab9f923eSPyun YongHyeon if (pci_get_device(dev) == RT_DEVICEID_8101E) {
151114013280SMarius Strobl /* RTL8106E(US), RTL8107E */
1512ab9f923eSPyun YongHyeon sc->rl_flags |= RL_FLAG_FASTETHER;
1513ab9f923eSPyun YongHyeon } else
1514ab9f923eSPyun YongHyeon sc->rl_flags |= RL_FLAG_JUMBOV2 | RL_FLAG_WOL_MANLINK;
1515ab9f923eSPyun YongHyeon
1516ab9f923eSPyun YongHyeon sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR |
1517ab9f923eSPyun YongHyeon RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP |
1518f1a5f291SMarius Strobl RL_FLAG_AUTOPAD | RL_FLAG_CMDSTOP_WAIT_TXQ |
151914013280SMarius Strobl RL_FLAG_8168G_PLUS;
1520ab9f923eSPyun YongHyeon break;
1521566ca8caSJung-uk Kim case RL_HWREV_8169_8110SB:
1522566ca8caSJung-uk Kim case RL_HWREV_8169_8110SBL:
1523566ca8caSJung-uk Kim case RL_HWREV_8169_8110SC:
1524566ca8caSJung-uk Kim case RL_HWREV_8169_8110SCE:
1525566ca8caSJung-uk Kim sc->rl_flags |= RL_FLAG_PHYWAKE;
1526566ca8caSJung-uk Kim /* FALLTHROUGH */
15270596d7e6SPyun YongHyeon case RL_HWREV_8169:
15280596d7e6SPyun YongHyeon case RL_HWREV_8169S:
1529566ca8caSJung-uk Kim case RL_HWREV_8110S:
1530566ca8caSJung-uk Kim sc->rl_flags |= RL_FLAG_MACRESET;
1531351a76f9SPyun YongHyeon break;
1532351a76f9SPyun YongHyeon default:
1533351a76f9SPyun YongHyeon break;
1534351a76f9SPyun YongHyeon }
1535351a76f9SPyun YongHyeon
1536e7e7593cSPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8139CPLUS) {
1537e7e7593cSPyun YongHyeon sc->rl_cfg0 = RL_8139_CFG0;
1538e7e7593cSPyun YongHyeon sc->rl_cfg1 = RL_8139_CFG1;
1539e7e7593cSPyun YongHyeon sc->rl_cfg2 = 0;
1540e7e7593cSPyun YongHyeon sc->rl_cfg3 = RL_8139_CFG3;
1541e7e7593cSPyun YongHyeon sc->rl_cfg4 = RL_8139_CFG4;
1542e7e7593cSPyun YongHyeon sc->rl_cfg5 = RL_8139_CFG5;
1543e7e7593cSPyun YongHyeon } else {
1544e7e7593cSPyun YongHyeon sc->rl_cfg0 = RL_CFG0;
1545e7e7593cSPyun YongHyeon sc->rl_cfg1 = RL_CFG1;
1546e7e7593cSPyun YongHyeon sc->rl_cfg2 = RL_CFG2;
1547e7e7593cSPyun YongHyeon sc->rl_cfg3 = RL_CFG3;
1548e7e7593cSPyun YongHyeon sc->rl_cfg4 = RL_CFG4;
1549e7e7593cSPyun YongHyeon sc->rl_cfg5 = RL_CFG5;
1550e7e7593cSPyun YongHyeon }
1551e7e7593cSPyun YongHyeon
155293252626SPyun YongHyeon /* Reset the adapter. */
155393252626SPyun YongHyeon RL_LOCK(sc);
155493252626SPyun YongHyeon re_reset(sc);
155593252626SPyun YongHyeon RL_UNLOCK(sc);
155693252626SPyun YongHyeon
1557deb5c680SPyun YongHyeon /* Enable PME. */
1558deb5c680SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
1559e7e7593cSPyun YongHyeon cfg = CSR_READ_1(sc, sc->rl_cfg1);
1560deb5c680SPyun YongHyeon cfg |= RL_CFG1_PME;
1561e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg1, cfg);
1562e7e7593cSPyun YongHyeon cfg = CSR_READ_1(sc, sc->rl_cfg5);
1563deb5c680SPyun YongHyeon cfg &= RL_CFG5_PME_STS;
1564e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg5, cfg);
1565deb5c680SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
1566deb5c680SPyun YongHyeon
1567deb5c680SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_PAR) != 0) {
1568deb5c680SPyun YongHyeon /*
1569deb5c680SPyun YongHyeon * XXX Should have a better way to extract station
1570deb5c680SPyun YongHyeon * address from EEPROM.
1571deb5c680SPyun YongHyeon */
1572deb5c680SPyun YongHyeon for (i = 0; i < ETHER_ADDR_LEN; i++)
1573deb5c680SPyun YongHyeon eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i);
1574deb5c680SPyun YongHyeon } else {
1575141f92e7SPyun YongHyeon sc->rl_eewidth = RL_9356_ADDR_LEN;
1576ed510fb0SBill Paul re_read_eeprom(sc, (caddr_t)&re_did, 0, 1);
1577a94100faSBill Paul if (re_did != 0x8129)
1578141f92e7SPyun YongHyeon sc->rl_eewidth = RL_9346_ADDR_LEN;
1579a94100faSBill Paul
1580a94100faSBill Paul /*
1581a94100faSBill Paul * Get station address from the EEPROM.
1582a94100faSBill Paul */
1583ed510fb0SBill Paul re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3);
1584be099007SPyun YongHyeon for (i = 0; i < ETHER_ADDR_LEN / 2; i++)
1585be099007SPyun YongHyeon as[i] = le16toh(as[i]);
1586de8925a2SKevin Lo bcopy(as, eaddr, ETHER_ADDR_LEN);
1587deb5c680SPyun YongHyeon }
1588ed510fb0SBill Paul
1589ed510fb0SBill Paul if (sc->rl_type == RL_8169) {
1590d65abd66SPyun YongHyeon /* Set RX length mask and number of descriptors. */
1591ed510fb0SBill Paul sc->rl_rxlenmask = RL_RDESC_STAT_GFRAGLEN;
1592ed510fb0SBill Paul sc->rl_txstart = RL_GTXSTART;
1593d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_desc_cnt = RL_8169_TX_DESC_CNT;
1594d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc_cnt = RL_8169_RX_DESC_CNT;
1595ed510fb0SBill Paul } else {
1596d65abd66SPyun YongHyeon /* Set RX length mask and number of descriptors. */
1597ed510fb0SBill Paul sc->rl_rxlenmask = RL_RDESC_STAT_FRAGLEN;
1598ed510fb0SBill Paul sc->rl_txstart = RL_TXSTART;
1599d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_desc_cnt = RL_8139_TX_DESC_CNT;
1600d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc_cnt = RL_8139_RX_DESC_CNT;
1601abc8ff44SBill Paul }
16029bac70b8SBill Paul
1603a94100faSBill Paul error = re_allocmem(dev, sc);
1604a94100faSBill Paul if (error)
1605a94100faSBill Paul goto fail;
16060534aae0SPyun YongHyeon re_add_sysctls(sc);
1607a94100faSBill Paul
1608cd036ec1SBrooks Davis ifp = sc->rl_ifp = if_alloc(IFT_ETHER);
1609cd036ec1SBrooks Davis
161061f45a72SPyun YongHyeon /* Take controller out of deep sleep mode. */
161161f45a72SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
161261f45a72SPyun YongHyeon if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
161361f45a72SPyun YongHyeon CSR_WRITE_1(sc, RL_GPIO,
161461f45a72SPyun YongHyeon CSR_READ_1(sc, RL_GPIO) | 0x01);
161561f45a72SPyun YongHyeon else
161661f45a72SPyun YongHyeon CSR_WRITE_1(sc, RL_GPIO,
161761f45a72SPyun YongHyeon CSR_READ_1(sc, RL_GPIO) & ~0x01);
161861f45a72SPyun YongHyeon }
161961f45a72SPyun YongHyeon
1620351a76f9SPyun YongHyeon /* Take PHY out of power down mode. */
162139e69201SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_PHYWAKE_PM) != 0) {
1622d0c45156SPyun YongHyeon CSR_WRITE_1(sc, RL_PMCH, CSR_READ_1(sc, RL_PMCH) | 0x80);
162339e69201SPyun YongHyeon if (hw_rev->rl_rev == RL_HWREV_8401E)
162439e69201SPyun YongHyeon CSR_WRITE_1(sc, 0xD1, CSR_READ_1(sc, 0xD1) & ~0x08);
162539e69201SPyun YongHyeon }
1626351a76f9SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_PHYWAKE) != 0) {
1627351a76f9SPyun YongHyeon re_gmii_writereg(dev, 1, 0x1f, 0);
1628351a76f9SPyun YongHyeon re_gmii_writereg(dev, 1, 0x0e, 0);
1629351a76f9SPyun YongHyeon }
1630351a76f9SPyun YongHyeon
16314519a073SJustin Hibbits if_setsoftc(ifp, sc);
16329bf40edeSBrooks Davis if_initname(ifp, device_get_name(dev), device_get_unit(dev));
16334519a073SJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
16344519a073SJustin Hibbits if_setioctlfn(ifp, re_ioctl);
16354519a073SJustin Hibbits if_setstartfn(ifp, re_start);
1636bc2a1002SPyun YongHyeon /*
1637bc2a1002SPyun YongHyeon * RTL8168/8111C generates wrong IP checksummed frame if the
163874a03446SPyun YongHyeon * packet has IP options so disable TX checksum offloading.
1639bc2a1002SPyun YongHyeon */
1640bc2a1002SPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8168C ||
16413c2a957dSPyun YongHyeon sc->rl_hwrev->rl_rev == RL_HWREV_8168C_SPIN2 ||
164274a03446SPyun YongHyeon sc->rl_hwrev->rl_rev == RL_HWREV_8168CP) {
16434519a073SJustin Hibbits if_sethwassist(ifp, 0);
16444519a073SJustin Hibbits if_setcapabilities(ifp, IFCAP_RXCSUM | IFCAP_TSO4);
164574a03446SPyun YongHyeon } else {
16464519a073SJustin Hibbits if_sethwassist(ifp, CSUM_IP | CSUM_TCP | CSUM_UDP);
16474519a073SJustin Hibbits if_setcapabilities(ifp, IFCAP_HWCSUM | IFCAP_TSO4);
164874a03446SPyun YongHyeon }
16494519a073SJustin Hibbits if_sethwassistbits(ifp, CSUM_TSO, 0);
16504519a073SJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
16514519a073SJustin Hibbits if_setinitfn(ifp, re_init);
16524519a073SJustin Hibbits if_setsendqlen(ifp, RL_IFQ_MAXLEN);
16534519a073SJustin Hibbits if_setsendqready(ifp);
1654a94100faSBill Paul
16556c3e93cbSGleb Smirnoff NET_TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc);
1656ed510fb0SBill Paul
1657fed3ed71SPyun YongHyeon #define RE_PHYAD_INTERNAL 0
1658fed3ed71SPyun YongHyeon
1659fed3ed71SPyun YongHyeon /* Do MII setup. */
1660fed3ed71SPyun YongHyeon phy = RE_PHYAD_INTERNAL;
1661fed3ed71SPyun YongHyeon if (sc->rl_type == RL_8169)
1662fed3ed71SPyun YongHyeon phy = 1;
166314013280SMarius Strobl capmask = BMSR_DEFCAPMASK;
166414013280SMarius Strobl if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0)
166514013280SMarius Strobl capmask &= ~BMSR_EXTSTAT;
1666fed3ed71SPyun YongHyeon error = mii_attach(dev, &sc->rl_miibus, ifp, re_ifmedia_upd,
166714013280SMarius Strobl re_ifmedia_sts, capmask, phy, MII_OFFSET_ANY, MIIF_DOPAUSE);
1668fed3ed71SPyun YongHyeon if (error != 0) {
1669fed3ed71SPyun YongHyeon device_printf(dev, "attaching PHYs failed\n");
1670fed3ed71SPyun YongHyeon goto fail;
1671fed3ed71SPyun YongHyeon }
1672fed3ed71SPyun YongHyeon
167355747938SEvgeni Golov /* If address was not found, create one based on the hostid and name. */
167455747938SEvgeni Golov if (ETHER_IS_ZERO(eaddr)) {
167555747938SEvgeni Golov ether_gen_addr(ifp, (struct ether_addr *)eaddr);
167655747938SEvgeni Golov }
167755747938SEvgeni Golov
1678a94100faSBill Paul /*
1679a94100faSBill Paul * Call MI attach routine.
1680a94100faSBill Paul */
1681a94100faSBill Paul ether_ifattach(ifp, eaddr);
1682a94100faSBill Paul
1683960fd5b3SPyun YongHyeon /* VLAN capability setup */
16844519a073SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING, 0);
16854519a073SJustin Hibbits if (if_getcapabilities(ifp) & IFCAP_HWCSUM)
16864519a073SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWCSUM, 0);
16877467bd53SPyun YongHyeon /* Enable WOL if PM is supported. */
1688*ddaf6524SJohn Baldwin if (pci_has_pm(sc->rl_dev))
16894519a073SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_WOL, 0);
16904519a073SJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
16914519a073SJustin Hibbits if_setcapenablebit(ifp, 0, (IFCAP_WOL_UCAST | IFCAP_WOL_MCAST));
1692a2a8420cSPyun YongHyeon /*
1693f9ad4da7SPyun YongHyeon * Don't enable TSO by default. It is known to generate
1694f9ad4da7SPyun YongHyeon * corrupted TCP segments(bad TCP options) under certain
16952df05392SSergey Kandaurov * circumstances.
1696a2a8420cSPyun YongHyeon */
16974519a073SJustin Hibbits if_sethwassistbits(ifp, 0, CSUM_TSO);
16984519a073SJustin Hibbits if_setcapenablebit(ifp, 0, (IFCAP_TSO4 | IFCAP_VLAN_HWTSO));
1699960fd5b3SPyun YongHyeon #ifdef DEVICE_POLLING
17004519a073SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
1701960fd5b3SPyun YongHyeon #endif
1702960fd5b3SPyun YongHyeon /*
1703960fd5b3SPyun YongHyeon * Tell the upper layer(s) we support long frames.
1704960fd5b3SPyun YongHyeon * Must appear after the call to ether_ifattach() because
1705960fd5b3SPyun YongHyeon * ether_ifattach() sets ifi_hdrlen to the default value.
1706960fd5b3SPyun YongHyeon */
17074519a073SJustin Hibbits if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
1708960fd5b3SPyun YongHyeon
1709579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
1710579a6e3cSLuigi Rizzo re_netmap_attach(sc);
1711579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
1712e9f8886eSMarius Strobl
1713ed510fb0SBill Paul #ifdef RE_DIAG
1714ed510fb0SBill Paul /*
1715ed510fb0SBill Paul * Perform hardware diagnostic on the original RTL8169.
1716ed510fb0SBill Paul * Some 32-bit cards were incorrectly wired and would
1717ed510fb0SBill Paul * malfunction if plugged into a 64-bit slot.
1718ed510fb0SBill Paul */
1719ed510fb0SBill Paul if (hwrev == RL_HWREV_8169) {
1720ed510fb0SBill Paul error = re_diag(sc);
1721a94100faSBill Paul if (error) {
1722ed510fb0SBill Paul device_printf(dev,
1723ed510fb0SBill Paul "attach aborted due to hardware diag failure\n");
1724a94100faSBill Paul ether_ifdetach(ifp);
1725a94100faSBill Paul goto fail;
1726a94100faSBill Paul }
1727ed510fb0SBill Paul }
1728ed510fb0SBill Paul #endif
1729a94100faSBill Paul
1730502be0f7SPyun YongHyeon #ifdef RE_TX_MODERATION
1731502be0f7SPyun YongHyeon intr_filter = 1;
1732502be0f7SPyun YongHyeon #endif
1733a94100faSBill Paul /* Hook interrupt last to avoid having to lock softc */
1734502be0f7SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 &&
1735502be0f7SPyun YongHyeon intr_filter == 0) {
1736502be0f7SPyun YongHyeon error = bus_setup_intr(dev, sc->rl_irq[0],
1737502be0f7SPyun YongHyeon INTR_TYPE_NET | INTR_MPSAFE, NULL, re_intr_msi, sc,
1738502be0f7SPyun YongHyeon &sc->rl_intrhand[0]);
1739502be0f7SPyun YongHyeon } else {
17405774c5ffSPyun YongHyeon error = bus_setup_intr(dev, sc->rl_irq[0],
17415774c5ffSPyun YongHyeon INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc,
17425774c5ffSPyun YongHyeon &sc->rl_intrhand[0]);
17435774c5ffSPyun YongHyeon }
1744a94100faSBill Paul if (error) {
1745d1754a9bSJohn Baldwin device_printf(dev, "couldn't set up irq\n");
1746a94100faSBill Paul ether_ifdetach(ifp);
1747306c97e2SMark Johnston goto fail;
1748a94100faSBill Paul }
1749a94100faSBill Paul
17507790c8c1SConrad Meyer DEBUGNET_SET(ifp, re);
1751306c97e2SMark Johnston
1752a94100faSBill Paul fail:
1753a94100faSBill Paul if (error)
1754a94100faSBill Paul re_detach(dev);
1755a94100faSBill Paul
1756a94100faSBill Paul return (error);
1757a94100faSBill Paul }
1758a94100faSBill Paul
1759a94100faSBill Paul /*
1760a94100faSBill Paul * Shutdown hardware and free up resources. This can be called any
1761a94100faSBill Paul * time after the mutex has been initialized. It is called in both
1762a94100faSBill Paul * the error case in attach and the normal detach case so it needs
1763a94100faSBill Paul * to be careful about only freeing resources that have actually been
1764a94100faSBill Paul * allocated.
1765a94100faSBill Paul */
1766a94100faSBill Paul static int
re_detach(device_t dev)17677b5ffebfSPyun YongHyeon re_detach(device_t dev)
1768a94100faSBill Paul {
1769a94100faSBill Paul struct rl_softc *sc;
17704519a073SJustin Hibbits if_t ifp;
17715774c5ffSPyun YongHyeon int i, rid;
1772a94100faSBill Paul
1773a94100faSBill Paul sc = device_get_softc(dev);
1774fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
1775aedd16d9SJohn-Mark Gurney KASSERT(mtx_initialized(&sc->rl_mtx), ("re mutex not initialized"));
177697b9d4baSJohn-Mark Gurney
177781cf2eb6SPyun YongHyeon /* These should only be active if attach succeeded */
177881cf2eb6SPyun YongHyeon if (device_is_attached(dev)) {
177940929967SGleb Smirnoff #ifdef DEVICE_POLLING
17804519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING)
178140929967SGleb Smirnoff ether_poll_deregister(ifp);
178240929967SGleb Smirnoff #endif
178397b9d4baSJohn-Mark Gurney RL_LOCK(sc);
178497b9d4baSJohn-Mark Gurney #if 0
178597b9d4baSJohn-Mark Gurney sc->suspended = 1;
178697b9d4baSJohn-Mark Gurney #endif
1787a94100faSBill Paul re_stop(sc);
1788525e6a87SRuslan Ermilov RL_UNLOCK(sc);
1789d1754a9bSJohn Baldwin callout_drain(&sc->rl_stat_callout);
17903d4c1b57SJohn Baldwin taskqueue_drain(taskqueue_fast, &sc->rl_inttask);
1791a94100faSBill Paul /*
1792a94100faSBill Paul * Force off the IFF_UP flag here, in case someone
1793a94100faSBill Paul * still had a BPF descriptor attached to this
179497b9d4baSJohn-Mark Gurney * interface. If they do, ether_ifdetach() will cause
1795a94100faSBill Paul * the BPF code to try and clear the promisc mode
1796a94100faSBill Paul * flag, which will bubble down to re_ioctl(),
1797a94100faSBill Paul * which will try to call re_init() again. This will
1798a94100faSBill Paul * turn the NIC back on and restart the MII ticker,
1799a94100faSBill Paul * which will panic the system when the kernel tries
1800a94100faSBill Paul * to invoke the re_tick() function that isn't there
1801a94100faSBill Paul * anymore.
1802a94100faSBill Paul */
18034519a073SJustin Hibbits if_setflagbits(ifp, 0, IFF_UP);
1804525e6a87SRuslan Ermilov ether_ifdetach(ifp);
1805a94100faSBill Paul }
1806a94100faSBill Paul bus_generic_detach(dev);
1807a94100faSBill Paul
180897b9d4baSJohn-Mark Gurney /*
180997b9d4baSJohn-Mark Gurney * The rest is resource deallocation, so we should already be
181097b9d4baSJohn-Mark Gurney * stopped here.
181197b9d4baSJohn-Mark Gurney */
181297b9d4baSJohn-Mark Gurney
1813502be0f7SPyun YongHyeon if (sc->rl_intrhand[0] != NULL) {
1814502be0f7SPyun YongHyeon bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]);
1815502be0f7SPyun YongHyeon sc->rl_intrhand[0] = NULL;
18165774c5ffSPyun YongHyeon }
181782242c11SKevin Lo if (ifp != NULL) {
181882242c11SKevin Lo #ifdef DEV_NETMAP
181982242c11SKevin Lo netmap_detach(ifp);
182082242c11SKevin Lo #endif /* DEV_NETMAP */
1821ad4f426eSWarner Losh if_free(ifp);
182282242c11SKevin Lo }
1823502be0f7SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0)
1824502be0f7SPyun YongHyeon rid = 0;
1825502be0f7SPyun YongHyeon else
1826502be0f7SPyun YongHyeon rid = 1;
18275774c5ffSPyun YongHyeon if (sc->rl_irq[0] != NULL) {
1828502be0f7SPyun YongHyeon bus_release_resource(dev, SYS_RES_IRQ, rid, sc->rl_irq[0]);
18295774c5ffSPyun YongHyeon sc->rl_irq[0] = NULL;
18305774c5ffSPyun YongHyeon }
1831502be0f7SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0)
18325774c5ffSPyun YongHyeon pci_release_msi(dev);
18334a58fd45SPyun YongHyeon if (sc->rl_res_pba) {
18344a58fd45SPyun YongHyeon rid = PCIR_BAR(4);
18354a58fd45SPyun YongHyeon bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->rl_res_pba);
18364a58fd45SPyun YongHyeon }
1837a94100faSBill Paul if (sc->rl_res)
1838ace7ed5dSPyun YongHyeon bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id,
1839ace7ed5dSPyun YongHyeon sc->rl_res);
1840a94100faSBill Paul
1841a94100faSBill Paul /* Unload and free the RX DMA ring memory and map */
1842a94100faSBill Paul
1843a94100faSBill Paul if (sc->rl_ldata.rl_rx_list_tag) {
1844068d8643SJohn Baldwin if (sc->rl_ldata.rl_rx_list_addr)
1845a94100faSBill Paul bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag,
1846a94100faSBill Paul sc->rl_ldata.rl_rx_list_map);
1847068d8643SJohn Baldwin if (sc->rl_ldata.rl_rx_list)
1848a94100faSBill Paul bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag,
1849a94100faSBill Paul sc->rl_ldata.rl_rx_list,
1850a94100faSBill Paul sc->rl_ldata.rl_rx_list_map);
1851a94100faSBill Paul bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag);
1852a94100faSBill Paul }
1853a94100faSBill Paul
1854a94100faSBill Paul /* Unload and free the TX DMA ring memory and map */
1855a94100faSBill Paul
1856a94100faSBill Paul if (sc->rl_ldata.rl_tx_list_tag) {
1857068d8643SJohn Baldwin if (sc->rl_ldata.rl_tx_list_addr)
1858a94100faSBill Paul bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag,
1859a94100faSBill Paul sc->rl_ldata.rl_tx_list_map);
1860068d8643SJohn Baldwin if (sc->rl_ldata.rl_tx_list)
1861a94100faSBill Paul bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag,
1862a94100faSBill Paul sc->rl_ldata.rl_tx_list,
1863a94100faSBill Paul sc->rl_ldata.rl_tx_list_map);
1864a94100faSBill Paul bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag);
1865a94100faSBill Paul }
1866a94100faSBill Paul
1867a94100faSBill Paul /* Destroy all the RX and TX buffer maps */
1868a94100faSBill Paul
1869d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_tx_mtag) {
18709e18005dSPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
18719e18005dSPyun YongHyeon if (sc->rl_ldata.rl_tx_desc[i].tx_dmamap)
1872d65abd66SPyun YongHyeon bus_dmamap_destroy(sc->rl_ldata.rl_tx_mtag,
1873d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_desc[i].tx_dmamap);
18749e18005dSPyun YongHyeon }
1875d65abd66SPyun YongHyeon bus_dma_tag_destroy(sc->rl_ldata.rl_tx_mtag);
1876d65abd66SPyun YongHyeon }
1877d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_rx_mtag) {
18789e18005dSPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
18799e18005dSPyun YongHyeon if (sc->rl_ldata.rl_rx_desc[i].rx_dmamap)
1880d65abd66SPyun YongHyeon bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag,
1881d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc[i].rx_dmamap);
18829e18005dSPyun YongHyeon }
1883d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_rx_sparemap)
1884d65abd66SPyun YongHyeon bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag,
1885d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_sparemap);
1886d65abd66SPyun YongHyeon bus_dma_tag_destroy(sc->rl_ldata.rl_rx_mtag);
1887a94100faSBill Paul }
188881eee0ebSPyun YongHyeon if (sc->rl_ldata.rl_jrx_mtag) {
188981eee0ebSPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
189081eee0ebSPyun YongHyeon if (sc->rl_ldata.rl_jrx_desc[i].rx_dmamap)
189181eee0ebSPyun YongHyeon bus_dmamap_destroy(sc->rl_ldata.rl_jrx_mtag,
189281eee0ebSPyun YongHyeon sc->rl_ldata.rl_jrx_desc[i].rx_dmamap);
189381eee0ebSPyun YongHyeon }
189481eee0ebSPyun YongHyeon if (sc->rl_ldata.rl_jrx_sparemap)
189581eee0ebSPyun YongHyeon bus_dmamap_destroy(sc->rl_ldata.rl_jrx_mtag,
189681eee0ebSPyun YongHyeon sc->rl_ldata.rl_jrx_sparemap);
189781eee0ebSPyun YongHyeon bus_dma_tag_destroy(sc->rl_ldata.rl_jrx_mtag);
189881eee0ebSPyun YongHyeon }
1899a94100faSBill Paul /* Unload and free the stats buffer and map */
1900a94100faSBill Paul
1901a94100faSBill Paul if (sc->rl_ldata.rl_stag) {
1902068d8643SJohn Baldwin if (sc->rl_ldata.rl_stats_addr)
1903a94100faSBill Paul bus_dmamap_unload(sc->rl_ldata.rl_stag,
1904a94100faSBill Paul sc->rl_ldata.rl_smap);
1905068d8643SJohn Baldwin if (sc->rl_ldata.rl_stats)
19060534aae0SPyun YongHyeon bus_dmamem_free(sc->rl_ldata.rl_stag,
19070534aae0SPyun YongHyeon sc->rl_ldata.rl_stats, sc->rl_ldata.rl_smap);
1908a94100faSBill Paul bus_dma_tag_destroy(sc->rl_ldata.rl_stag);
1909a94100faSBill Paul }
1910a94100faSBill Paul
1911a94100faSBill Paul if (sc->rl_parent_tag)
1912a94100faSBill Paul bus_dma_tag_destroy(sc->rl_parent_tag);
1913a94100faSBill Paul
1914a94100faSBill Paul mtx_destroy(&sc->rl_mtx);
1915a94100faSBill Paul
1916a94100faSBill Paul return (0);
1917a94100faSBill Paul }
1918a94100faSBill Paul
1919d65abd66SPyun YongHyeon static __inline void
re_discard_rxbuf(struct rl_softc * sc,int idx)19207b5ffebfSPyun YongHyeon re_discard_rxbuf(struct rl_softc *sc, int idx)
1921a94100faSBill Paul {
1922d65abd66SPyun YongHyeon struct rl_desc *desc;
1923d65abd66SPyun YongHyeon struct rl_rxdesc *rxd;
1924d65abd66SPyun YongHyeon uint32_t cmdstat;
1925a94100faSBill Paul
19264519a073SJustin Hibbits if (if_getmtu(sc->rl_ifp) > RL_MTU &&
192781eee0ebSPyun YongHyeon (sc->rl_flags & RL_FLAG_JUMBOV2) != 0)
192881eee0ebSPyun YongHyeon rxd = &sc->rl_ldata.rl_jrx_desc[idx];
192981eee0ebSPyun YongHyeon else
1930d65abd66SPyun YongHyeon rxd = &sc->rl_ldata.rl_rx_desc[idx];
1931d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_rx_list[idx];
1932d65abd66SPyun YongHyeon desc->rl_vlanctl = 0;
1933d65abd66SPyun YongHyeon cmdstat = rxd->rx_size;
1934d65abd66SPyun YongHyeon if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1)
1935d65abd66SPyun YongHyeon cmdstat |= RL_RDESC_CMD_EOR;
1936d65abd66SPyun YongHyeon desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN);
1937d65abd66SPyun YongHyeon }
1938d65abd66SPyun YongHyeon
1939d65abd66SPyun YongHyeon static int
re_newbuf(struct rl_softc * sc,int idx)19407b5ffebfSPyun YongHyeon re_newbuf(struct rl_softc *sc, int idx)
1941d65abd66SPyun YongHyeon {
1942d65abd66SPyun YongHyeon struct mbuf *m;
1943d65abd66SPyun YongHyeon struct rl_rxdesc *rxd;
1944d65abd66SPyun YongHyeon bus_dma_segment_t segs[1];
1945d65abd66SPyun YongHyeon bus_dmamap_t map;
1946d65abd66SPyun YongHyeon struct rl_desc *desc;
1947d65abd66SPyun YongHyeon uint32_t cmdstat;
1948d65abd66SPyun YongHyeon int error, nsegs;
1949d65abd66SPyun YongHyeon
1950c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1951d65abd66SPyun YongHyeon if (m == NULL)
1952a94100faSBill Paul return (ENOBUFS);
1953a94100faSBill Paul
1954a94100faSBill Paul m->m_len = m->m_pkthdr.len = MCLBYTES;
195522a11c96SJohn-Mark Gurney #ifdef RE_FIXUP_RX
195622a11c96SJohn-Mark Gurney /*
195722a11c96SJohn-Mark Gurney * This is part of an evil trick to deal with non-x86 platforms.
195822a11c96SJohn-Mark Gurney * The RealTek chip requires RX buffers to be aligned on 64-bit
195922a11c96SJohn-Mark Gurney * boundaries, but that will hose non-x86 machines. To get around
196022a11c96SJohn-Mark Gurney * this, we leave some empty space at the start of each buffer
196122a11c96SJohn-Mark Gurney * and for non-x86 hosts, we copy the buffer back six bytes
196222a11c96SJohn-Mark Gurney * to achieve word alignment. This is slightly more efficient
196322a11c96SJohn-Mark Gurney * than allocating a new buffer, copying the contents, and
196422a11c96SJohn-Mark Gurney * discarding the old buffer.
196522a11c96SJohn-Mark Gurney */
196622a11c96SJohn-Mark Gurney m_adj(m, RE_ETHER_ALIGN);
196722a11c96SJohn-Mark Gurney #endif
1968d65abd66SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_rx_mtag,
1969d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
1970d65abd66SPyun YongHyeon if (error != 0) {
1971d65abd66SPyun YongHyeon m_freem(m);
1972d65abd66SPyun YongHyeon return (ENOBUFS);
1973d65abd66SPyun YongHyeon }
1974d65abd66SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs));
1975a94100faSBill Paul
1976d65abd66SPyun YongHyeon rxd = &sc->rl_ldata.rl_rx_desc[idx];
1977d65abd66SPyun YongHyeon if (rxd->rx_m != NULL) {
1978d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap,
1979d65abd66SPyun YongHyeon BUS_DMASYNC_POSTREAD);
1980d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap);
1981a94100faSBill Paul }
1982a94100faSBill Paul
1983d65abd66SPyun YongHyeon rxd->rx_m = m;
1984d65abd66SPyun YongHyeon map = rxd->rx_dmamap;
1985d65abd66SPyun YongHyeon rxd->rx_dmamap = sc->rl_ldata.rl_rx_sparemap;
1986d65abd66SPyun YongHyeon rxd->rx_size = segs[0].ds_len;
1987d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_sparemap = map;
1988d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap,
1989a94100faSBill Paul BUS_DMASYNC_PREREAD);
1990a94100faSBill Paul
1991d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_rx_list[idx];
1992d65abd66SPyun YongHyeon desc->rl_vlanctl = 0;
1993d65abd66SPyun YongHyeon desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[0].ds_addr));
1994d65abd66SPyun YongHyeon desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[0].ds_addr));
1995d65abd66SPyun YongHyeon cmdstat = segs[0].ds_len;
1996d65abd66SPyun YongHyeon if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1)
1997d65abd66SPyun YongHyeon cmdstat |= RL_RDESC_CMD_EOR;
1998d65abd66SPyun YongHyeon desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN);
1999d65abd66SPyun YongHyeon
2000a94100faSBill Paul return (0);
2001a94100faSBill Paul }
2002a94100faSBill Paul
200381eee0ebSPyun YongHyeon static int
re_jumbo_newbuf(struct rl_softc * sc,int idx)200481eee0ebSPyun YongHyeon re_jumbo_newbuf(struct rl_softc *sc, int idx)
200581eee0ebSPyun YongHyeon {
200681eee0ebSPyun YongHyeon struct mbuf *m;
200781eee0ebSPyun YongHyeon struct rl_rxdesc *rxd;
200881eee0ebSPyun YongHyeon bus_dma_segment_t segs[1];
200981eee0ebSPyun YongHyeon bus_dmamap_t map;
201081eee0ebSPyun YongHyeon struct rl_desc *desc;
201181eee0ebSPyun YongHyeon uint32_t cmdstat;
201281eee0ebSPyun YongHyeon int error, nsegs;
201381eee0ebSPyun YongHyeon
2014c6499eccSGleb Smirnoff m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM9BYTES);
201581eee0ebSPyun YongHyeon if (m == NULL)
201681eee0ebSPyun YongHyeon return (ENOBUFS);
201781eee0ebSPyun YongHyeon m->m_len = m->m_pkthdr.len = MJUM9BYTES;
201881eee0ebSPyun YongHyeon #ifdef RE_FIXUP_RX
201981eee0ebSPyun YongHyeon m_adj(m, RE_ETHER_ALIGN);
202081eee0ebSPyun YongHyeon #endif
202181eee0ebSPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_jrx_mtag,
202281eee0ebSPyun YongHyeon sc->rl_ldata.rl_jrx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
202381eee0ebSPyun YongHyeon if (error != 0) {
202481eee0ebSPyun YongHyeon m_freem(m);
202581eee0ebSPyun YongHyeon return (ENOBUFS);
202681eee0ebSPyun YongHyeon }
202781eee0ebSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs));
202881eee0ebSPyun YongHyeon
202981eee0ebSPyun YongHyeon rxd = &sc->rl_ldata.rl_jrx_desc[idx];
203081eee0ebSPyun YongHyeon if (rxd->rx_m != NULL) {
203181eee0ebSPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_jrx_mtag, rxd->rx_dmamap,
203281eee0ebSPyun YongHyeon BUS_DMASYNC_POSTREAD);
203381eee0ebSPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_jrx_mtag, rxd->rx_dmamap);
203481eee0ebSPyun YongHyeon }
203581eee0ebSPyun YongHyeon
203681eee0ebSPyun YongHyeon rxd->rx_m = m;
203781eee0ebSPyun YongHyeon map = rxd->rx_dmamap;
203881eee0ebSPyun YongHyeon rxd->rx_dmamap = sc->rl_ldata.rl_jrx_sparemap;
203981eee0ebSPyun YongHyeon rxd->rx_size = segs[0].ds_len;
204081eee0ebSPyun YongHyeon sc->rl_ldata.rl_jrx_sparemap = map;
204181eee0ebSPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_jrx_mtag, rxd->rx_dmamap,
204281eee0ebSPyun YongHyeon BUS_DMASYNC_PREREAD);
204381eee0ebSPyun YongHyeon
204481eee0ebSPyun YongHyeon desc = &sc->rl_ldata.rl_rx_list[idx];
204581eee0ebSPyun YongHyeon desc->rl_vlanctl = 0;
204681eee0ebSPyun YongHyeon desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[0].ds_addr));
204781eee0ebSPyun YongHyeon desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[0].ds_addr));
204881eee0ebSPyun YongHyeon cmdstat = segs[0].ds_len;
204981eee0ebSPyun YongHyeon if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1)
205081eee0ebSPyun YongHyeon cmdstat |= RL_RDESC_CMD_EOR;
205181eee0ebSPyun YongHyeon desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN);
205281eee0ebSPyun YongHyeon
205381eee0ebSPyun YongHyeon return (0);
205481eee0ebSPyun YongHyeon }
205581eee0ebSPyun YongHyeon
205622a11c96SJohn-Mark Gurney #ifdef RE_FIXUP_RX
205722a11c96SJohn-Mark Gurney static __inline void
re_fixup_rx(struct mbuf * m)20587b5ffebfSPyun YongHyeon re_fixup_rx(struct mbuf *m)
205922a11c96SJohn-Mark Gurney {
206022a11c96SJohn-Mark Gurney int i;
206122a11c96SJohn-Mark Gurney uint16_t *src, *dst;
206222a11c96SJohn-Mark Gurney
206322a11c96SJohn-Mark Gurney src = mtod(m, uint16_t *);
206422a11c96SJohn-Mark Gurney dst = src - (RE_ETHER_ALIGN - ETHER_ALIGN) / sizeof *src;
206522a11c96SJohn-Mark Gurney
206622a11c96SJohn-Mark Gurney for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
206722a11c96SJohn-Mark Gurney *dst++ = *src++;
206822a11c96SJohn-Mark Gurney
206922a11c96SJohn-Mark Gurney m->m_data -= RE_ETHER_ALIGN - ETHER_ALIGN;
207022a11c96SJohn-Mark Gurney }
207122a11c96SJohn-Mark Gurney #endif
207222a11c96SJohn-Mark Gurney
2073a94100faSBill Paul static int
re_tx_list_init(struct rl_softc * sc)20747b5ffebfSPyun YongHyeon re_tx_list_init(struct rl_softc *sc)
2075a94100faSBill Paul {
2076d65abd66SPyun YongHyeon struct rl_desc *desc;
2077d65abd66SPyun YongHyeon int i;
207897b9d4baSJohn-Mark Gurney
207997b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
208097b9d4baSJohn-Mark Gurney
2081d65abd66SPyun YongHyeon bzero(sc->rl_ldata.rl_tx_list,
2082d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc));
2083d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++)
2084d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_desc[i].tx_m = NULL;
2085579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
2086579a6e3cSLuigi Rizzo re_netmap_tx_init(sc);
2087579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
2088d65abd66SPyun YongHyeon /* Set EOR. */
2089d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_tx_list[sc->rl_ldata.rl_tx_desc_cnt - 1];
2090d65abd66SPyun YongHyeon desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOR);
2091a94100faSBill Paul
2092a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
2093d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_list_map,
2094d65abd66SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2095d65abd66SPyun YongHyeon
2096a94100faSBill Paul sc->rl_ldata.rl_tx_prodidx = 0;
2097a94100faSBill Paul sc->rl_ldata.rl_tx_considx = 0;
2098d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_free = sc->rl_ldata.rl_tx_desc_cnt;
2099a94100faSBill Paul
2100a94100faSBill Paul return (0);
2101a94100faSBill Paul }
2102a94100faSBill Paul
2103a94100faSBill Paul static int
re_rx_list_init(struct rl_softc * sc)21047b5ffebfSPyun YongHyeon re_rx_list_init(struct rl_softc *sc)
2105a94100faSBill Paul {
2106d65abd66SPyun YongHyeon int error, i;
2107a94100faSBill Paul
2108d65abd66SPyun YongHyeon bzero(sc->rl_ldata.rl_rx_list,
2109d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc));
2110d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
2111d65abd66SPyun YongHyeon sc->rl_ldata.rl_rx_desc[i].rx_m = NULL;
2112d65abd66SPyun YongHyeon if ((error = re_newbuf(sc, i)) != 0)
2113d65abd66SPyun YongHyeon return (error);
2114a94100faSBill Paul }
2115579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
2116579a6e3cSLuigi Rizzo re_netmap_rx_init(sc);
2117579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
2118a94100faSBill Paul
2119a94100faSBill Paul /* Flush the RX descriptors */
2120a94100faSBill Paul
2121a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
2122a94100faSBill Paul sc->rl_ldata.rl_rx_list_map,
2123a94100faSBill Paul BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
2124a94100faSBill Paul
2125a94100faSBill Paul sc->rl_ldata.rl_rx_prodidx = 0;
2126a94100faSBill Paul sc->rl_head = sc->rl_tail = NULL;
2127502be0f7SPyun YongHyeon sc->rl_int_rx_act = 0;
2128a94100faSBill Paul
2129a94100faSBill Paul return (0);
2130a94100faSBill Paul }
2131a94100faSBill Paul
213281eee0ebSPyun YongHyeon static int
re_jrx_list_init(struct rl_softc * sc)213381eee0ebSPyun YongHyeon re_jrx_list_init(struct rl_softc *sc)
213481eee0ebSPyun YongHyeon {
213581eee0ebSPyun YongHyeon int error, i;
213681eee0ebSPyun YongHyeon
213781eee0ebSPyun YongHyeon bzero(sc->rl_ldata.rl_rx_list,
213881eee0ebSPyun YongHyeon sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc));
213981eee0ebSPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
214081eee0ebSPyun YongHyeon sc->rl_ldata.rl_jrx_desc[i].rx_m = NULL;
214181eee0ebSPyun YongHyeon if ((error = re_jumbo_newbuf(sc, i)) != 0)
214281eee0ebSPyun YongHyeon return (error);
214381eee0ebSPyun YongHyeon }
214481eee0ebSPyun YongHyeon
214581eee0ebSPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
214681eee0ebSPyun YongHyeon sc->rl_ldata.rl_rx_list_map,
214781eee0ebSPyun YongHyeon BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
214881eee0ebSPyun YongHyeon
214981eee0ebSPyun YongHyeon sc->rl_ldata.rl_rx_prodidx = 0;
215081eee0ebSPyun YongHyeon sc->rl_head = sc->rl_tail = NULL;
2151502be0f7SPyun YongHyeon sc->rl_int_rx_act = 0;
215281eee0ebSPyun YongHyeon
215381eee0ebSPyun YongHyeon return (0);
215481eee0ebSPyun YongHyeon }
215581eee0ebSPyun YongHyeon
2156a94100faSBill Paul /*
2157a94100faSBill Paul * RX handler for C+ and 8169. For the gigE chips, we support
2158a94100faSBill Paul * the reception of jumbo frames that have been fragmented
2159a94100faSBill Paul * across multiple 2K mbuf cluster buffers.
2160a94100faSBill Paul */
2161ed510fb0SBill Paul static int
re_rxeof(struct rl_softc * sc,int * rx_npktsp)21621abcdbd1SAttilio Rao re_rxeof(struct rl_softc *sc, int *rx_npktsp)
2163a94100faSBill Paul {
2164a94100faSBill Paul struct mbuf *m;
21654519a073SJustin Hibbits if_t ifp;
216681eee0ebSPyun YongHyeon int i, rxerr, total_len;
2167a94100faSBill Paul struct rl_desc *cur_rx;
2168a94100faSBill Paul u_int32_t rxstat, rxvlan;
216981eee0ebSPyun YongHyeon int jumbo, maxpkt = 16, rx_npkts = 0;
2170a94100faSBill Paul
21715120abbfSSam Leffler RL_LOCK_ASSERT(sc);
21725120abbfSSam Leffler
2173fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
2174579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
2175ce3ee1e7SLuigi Rizzo if (netmap_rx_irq(ifp, 0, &rx_npkts))
2176579a6e3cSLuigi Rizzo return 0;
2177579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
21784519a073SJustin Hibbits if (if_getmtu(ifp) > RL_MTU && (sc->rl_flags & RL_FLAG_JUMBOV2) != 0)
217981eee0ebSPyun YongHyeon jumbo = 1;
218081eee0ebSPyun YongHyeon else
218181eee0ebSPyun YongHyeon jumbo = 0;
2182a94100faSBill Paul
2183a94100faSBill Paul /* Invalidate the descriptor memory */
2184a94100faSBill Paul
2185a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
2186a94100faSBill Paul sc->rl_ldata.rl_rx_list_map,
2187d65abd66SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2188a94100faSBill Paul
2189d65abd66SPyun YongHyeon for (i = sc->rl_ldata.rl_rx_prodidx; maxpkt > 0;
2190d65abd66SPyun YongHyeon i = RL_RX_DESC_NXT(sc, i)) {
21914519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
21925b6d1d9dSPyun YongHyeon break;
2193a94100faSBill Paul cur_rx = &sc->rl_ldata.rl_rx_list[i];
2194a94100faSBill Paul rxstat = le32toh(cur_rx->rl_cmdstat);
2195d65abd66SPyun YongHyeon if ((rxstat & RL_RDESC_STAT_OWN) != 0)
2196d65abd66SPyun YongHyeon break;
2197d65abd66SPyun YongHyeon total_len = rxstat & sc->rl_rxlenmask;
2198a94100faSBill Paul rxvlan = le32toh(cur_rx->rl_vlanctl);
219981eee0ebSPyun YongHyeon if (jumbo != 0)
220081eee0ebSPyun YongHyeon m = sc->rl_ldata.rl_jrx_desc[i].rx_m;
220181eee0ebSPyun YongHyeon else
2202d65abd66SPyun YongHyeon m = sc->rl_ldata.rl_rx_desc[i].rx_m;
2203a94100faSBill Paul
220481eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
220581eee0ebSPyun YongHyeon (rxstat & (RL_RDESC_STAT_SOF | RL_RDESC_STAT_EOF)) !=
220681eee0ebSPyun YongHyeon (RL_RDESC_STAT_SOF | RL_RDESC_STAT_EOF)) {
220781eee0ebSPyun YongHyeon /*
220881eee0ebSPyun YongHyeon * RTL8168C or later controllers do not
220981eee0ebSPyun YongHyeon * support multi-fragment packet.
221081eee0ebSPyun YongHyeon */
221181eee0ebSPyun YongHyeon re_discard_rxbuf(sc, i);
221281eee0ebSPyun YongHyeon continue;
221381eee0ebSPyun YongHyeon } else if ((rxstat & RL_RDESC_STAT_EOF) == 0) {
2214d65abd66SPyun YongHyeon if (re_newbuf(sc, i) != 0) {
2215d65abd66SPyun YongHyeon /*
2216d65abd66SPyun YongHyeon * If this is part of a multi-fragment packet,
2217d65abd66SPyun YongHyeon * discard all the pieces.
2218d65abd66SPyun YongHyeon */
2219d65abd66SPyun YongHyeon if (sc->rl_head != NULL) {
2220d65abd66SPyun YongHyeon m_freem(sc->rl_head);
2221d65abd66SPyun YongHyeon sc->rl_head = sc->rl_tail = NULL;
2222d65abd66SPyun YongHyeon }
2223d65abd66SPyun YongHyeon re_discard_rxbuf(sc, i);
2224d65abd66SPyun YongHyeon continue;
2225d65abd66SPyun YongHyeon }
222622a11c96SJohn-Mark Gurney m->m_len = RE_RX_DESC_BUFLEN;
2227a94100faSBill Paul if (sc->rl_head == NULL)
2228a94100faSBill Paul sc->rl_head = sc->rl_tail = m;
2229a94100faSBill Paul else {
2230a94100faSBill Paul m->m_flags &= ~M_PKTHDR;
2231a94100faSBill Paul sc->rl_tail->m_next = m;
2232a94100faSBill Paul sc->rl_tail = m;
2233a94100faSBill Paul }
2234a94100faSBill Paul continue;
2235a94100faSBill Paul }
2236a94100faSBill Paul
2237a94100faSBill Paul /*
2238a94100faSBill Paul * NOTE: for the 8139C+, the frame length field
2239a94100faSBill Paul * is always 12 bits in size, but for the gigE chips,
2240a94100faSBill Paul * it is 13 bits (since the max RX frame length is 16K).
2241a94100faSBill Paul * Unfortunately, all 32 bits in the status word
2242a94100faSBill Paul * were already used, so to make room for the extra
2243a94100faSBill Paul * length bit, RealTek took out the 'frame alignment
2244a94100faSBill Paul * error' bit and shifted the other status bits
2245a94100faSBill Paul * over one slot. The OWN, EOR, FS and LS bits are
2246a94100faSBill Paul * still in the same places. We have already extracted
2247a94100faSBill Paul * the frame length and checked the OWN bit, so rather
2248a94100faSBill Paul * than using an alternate bit mapping, we shift the
2249a94100faSBill Paul * status bits one space to the right so we can evaluate
2250a94100faSBill Paul * them using the 8169 status as though it was in the
2251a94100faSBill Paul * same format as that of the 8139C+.
2252a94100faSBill Paul */
2253a94100faSBill Paul if (sc->rl_type == RL_8169)
2254a94100faSBill Paul rxstat >>= 1;
2255a94100faSBill Paul
225622a11c96SJohn-Mark Gurney /*
225722a11c96SJohn-Mark Gurney * if total_len > 2^13-1, both _RXERRSUM and _GIANT will be
225822a11c96SJohn-Mark Gurney * set, but if CRC is clear, it will still be a valid frame.
225922a11c96SJohn-Mark Gurney */
226081eee0ebSPyun YongHyeon if ((rxstat & RL_RDESC_STAT_RXERRSUM) != 0) {
226181eee0ebSPyun YongHyeon rxerr = 1;
226281eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) == 0 &&
226381eee0ebSPyun YongHyeon total_len > 8191 &&
226481eee0ebSPyun YongHyeon (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)
226581eee0ebSPyun YongHyeon rxerr = 0;
226681eee0ebSPyun YongHyeon if (rxerr != 0) {
2267c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2268a94100faSBill Paul /*
2269a94100faSBill Paul * If this is part of a multi-fragment packet,
2270a94100faSBill Paul * discard all the pieces.
2271a94100faSBill Paul */
2272a94100faSBill Paul if (sc->rl_head != NULL) {
2273a94100faSBill Paul m_freem(sc->rl_head);
2274a94100faSBill Paul sc->rl_head = sc->rl_tail = NULL;
2275a94100faSBill Paul }
2276d65abd66SPyun YongHyeon re_discard_rxbuf(sc, i);
2277a94100faSBill Paul continue;
2278a94100faSBill Paul }
227981eee0ebSPyun YongHyeon }
2280a94100faSBill Paul
2281a94100faSBill Paul /*
2282a94100faSBill Paul * If allocating a replacement mbuf fails,
2283a94100faSBill Paul * reload the current one.
2284a94100faSBill Paul */
228581eee0ebSPyun YongHyeon if (jumbo != 0)
228681eee0ebSPyun YongHyeon rxerr = re_jumbo_newbuf(sc, i);
228781eee0ebSPyun YongHyeon else
228881eee0ebSPyun YongHyeon rxerr = re_newbuf(sc, i);
228981eee0ebSPyun YongHyeon if (rxerr != 0) {
2290c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
2291a94100faSBill Paul if (sc->rl_head != NULL) {
2292a94100faSBill Paul m_freem(sc->rl_head);
2293a94100faSBill Paul sc->rl_head = sc->rl_tail = NULL;
2294a94100faSBill Paul }
2295d65abd66SPyun YongHyeon re_discard_rxbuf(sc, i);
2296a94100faSBill Paul continue;
2297a94100faSBill Paul }
2298a94100faSBill Paul
2299a94100faSBill Paul if (sc->rl_head != NULL) {
230081eee0ebSPyun YongHyeon if (jumbo != 0)
230181eee0ebSPyun YongHyeon m->m_len = total_len;
230281eee0ebSPyun YongHyeon else {
230322a11c96SJohn-Mark Gurney m->m_len = total_len % RE_RX_DESC_BUFLEN;
230422a11c96SJohn-Mark Gurney if (m->m_len == 0)
230522a11c96SJohn-Mark Gurney m->m_len = RE_RX_DESC_BUFLEN;
230681eee0ebSPyun YongHyeon }
2307a94100faSBill Paul /*
2308a94100faSBill Paul * Special case: if there's 4 bytes or less
2309a94100faSBill Paul * in this buffer, the mbuf can be discarded:
2310a94100faSBill Paul * the last 4 bytes is the CRC, which we don't
2311a94100faSBill Paul * care about anyway.
2312a94100faSBill Paul */
2313a94100faSBill Paul if (m->m_len <= ETHER_CRC_LEN) {
2314a94100faSBill Paul sc->rl_tail->m_len -=
2315a94100faSBill Paul (ETHER_CRC_LEN - m->m_len);
2316a94100faSBill Paul m_freem(m);
2317a94100faSBill Paul } else {
2318a94100faSBill Paul m->m_len -= ETHER_CRC_LEN;
2319a94100faSBill Paul m->m_flags &= ~M_PKTHDR;
2320a94100faSBill Paul sc->rl_tail->m_next = m;
2321a94100faSBill Paul }
2322a94100faSBill Paul m = sc->rl_head;
2323a94100faSBill Paul sc->rl_head = sc->rl_tail = NULL;
2324a94100faSBill Paul m->m_pkthdr.len = total_len - ETHER_CRC_LEN;
2325a94100faSBill Paul } else
2326a94100faSBill Paul m->m_pkthdr.len = m->m_len =
2327a94100faSBill Paul (total_len - ETHER_CRC_LEN);
2328a94100faSBill Paul
232922a11c96SJohn-Mark Gurney #ifdef RE_FIXUP_RX
233022a11c96SJohn-Mark Gurney re_fixup_rx(m);
233122a11c96SJohn-Mark Gurney #endif
2332c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
2333a94100faSBill Paul m->m_pkthdr.rcvif = ifp;
2334a94100faSBill Paul
2335a94100faSBill Paul /* Do RX checksumming if enabled */
2336a94100faSBill Paul
23374519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
2338deb5c680SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
2339a94100faSBill Paul /* Check IP header checksum */
2340a94100faSBill Paul if (rxstat & RL_RDESC_STAT_PROTOID)
2341deb5c680SPyun YongHyeon m->m_pkthdr.csum_flags |=
2342deb5c680SPyun YongHyeon CSUM_IP_CHECKED;
2343a94100faSBill Paul if (!(rxstat & RL_RDESC_STAT_IPSUMBAD))
2344deb5c680SPyun YongHyeon m->m_pkthdr.csum_flags |=
2345deb5c680SPyun YongHyeon CSUM_IP_VALID;
2346a94100faSBill Paul
2347a94100faSBill Paul /* Check TCP/UDP checksum */
2348a94100faSBill Paul if ((RL_TCPPKT(rxstat) &&
2349a94100faSBill Paul !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) ||
2350a94100faSBill Paul (RL_UDPPKT(rxstat) &&
2351a94100faSBill Paul !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) {
2352a94100faSBill Paul m->m_pkthdr.csum_flags |=
2353a94100faSBill Paul CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
2354a94100faSBill Paul m->m_pkthdr.csum_data = 0xffff;
2355a94100faSBill Paul }
2356deb5c680SPyun YongHyeon } else {
2357deb5c680SPyun YongHyeon /*
2358deb5c680SPyun YongHyeon * RTL8168C/RTL816CP/RTL8111C/RTL8111CP
2359deb5c680SPyun YongHyeon */
2360deb5c680SPyun YongHyeon if ((rxstat & RL_RDESC_STAT_PROTOID) &&
2361deb5c680SPyun YongHyeon (rxvlan & RL_RDESC_IPV4))
2362deb5c680SPyun YongHyeon m->m_pkthdr.csum_flags |=
2363deb5c680SPyun YongHyeon CSUM_IP_CHECKED;
2364deb5c680SPyun YongHyeon if (!(rxstat & RL_RDESC_STAT_IPSUMBAD) &&
2365deb5c680SPyun YongHyeon (rxvlan & RL_RDESC_IPV4))
2366deb5c680SPyun YongHyeon m->m_pkthdr.csum_flags |=
2367deb5c680SPyun YongHyeon CSUM_IP_VALID;
2368deb5c680SPyun YongHyeon if (((rxstat & RL_RDESC_STAT_TCP) &&
2369deb5c680SPyun YongHyeon !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) ||
2370deb5c680SPyun YongHyeon ((rxstat & RL_RDESC_STAT_UDP) &&
2371deb5c680SPyun YongHyeon !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) {
2372deb5c680SPyun YongHyeon m->m_pkthdr.csum_flags |=
2373deb5c680SPyun YongHyeon CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
2374deb5c680SPyun YongHyeon m->m_pkthdr.csum_data = 0xffff;
2375deb5c680SPyun YongHyeon }
2376deb5c680SPyun YongHyeon }
2377a94100faSBill Paul }
2378ed510fb0SBill Paul maxpkt--;
2379d147662cSGleb Smirnoff if (rxvlan & RL_RDESC_VLANCTL_TAG) {
238078ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag =
2381bddff934SPyun YongHyeon bswap16((rxvlan & RL_RDESC_VLANCTL_DATA));
238278ba57b9SAndre Oppermann m->m_flags |= M_VLANTAG;
2383d147662cSGleb Smirnoff }
23845120abbfSSam Leffler RL_UNLOCK(sc);
23854519a073SJustin Hibbits if_input(ifp, m);
23865120abbfSSam Leffler RL_LOCK(sc);
23871abcdbd1SAttilio Rao rx_npkts++;
2388a94100faSBill Paul }
2389a94100faSBill Paul
2390a94100faSBill Paul /* Flush the RX DMA ring */
2391a94100faSBill Paul
2392a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag,
2393a94100faSBill Paul sc->rl_ldata.rl_rx_list_map,
2394a94100faSBill Paul BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
2395a94100faSBill Paul
2396a94100faSBill Paul sc->rl_ldata.rl_rx_prodidx = i;
2397ed510fb0SBill Paul
23981abcdbd1SAttilio Rao if (rx_npktsp != NULL)
23991abcdbd1SAttilio Rao *rx_npktsp = rx_npkts;
2400ed510fb0SBill Paul if (maxpkt)
2401ed510fb0SBill Paul return (EAGAIN);
2402ed510fb0SBill Paul
2403ed510fb0SBill Paul return (0);
2404a94100faSBill Paul }
2405a94100faSBill Paul
2406a94100faSBill Paul static void
re_txeof(struct rl_softc * sc)24077b5ffebfSPyun YongHyeon re_txeof(struct rl_softc *sc)
2408a94100faSBill Paul {
24094519a073SJustin Hibbits if_t ifp;
2410d65abd66SPyun YongHyeon struct rl_txdesc *txd;
2411a94100faSBill Paul u_int32_t txstat;
2412d65abd66SPyun YongHyeon int cons;
2413d65abd66SPyun YongHyeon
2414d65abd66SPyun YongHyeon cons = sc->rl_ldata.rl_tx_considx;
2415d65abd66SPyun YongHyeon if (cons == sc->rl_ldata.rl_tx_prodidx)
2416d65abd66SPyun YongHyeon return;
2417a94100faSBill Paul
2418fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
2419579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
2420ce3ee1e7SLuigi Rizzo if (netmap_tx_irq(ifp, 0))
2421579a6e3cSLuigi Rizzo return;
2422579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
2423a94100faSBill Paul /* Invalidate the TX descriptor list */
2424a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
2425a94100faSBill Paul sc->rl_ldata.rl_tx_list_map,
2426d65abd66SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2427a94100faSBill Paul
2428d65abd66SPyun YongHyeon for (; cons != sc->rl_ldata.rl_tx_prodidx;
2429d65abd66SPyun YongHyeon cons = RL_TX_DESC_NXT(sc, cons)) {
2430d65abd66SPyun YongHyeon txstat = le32toh(sc->rl_ldata.rl_tx_list[cons].rl_cmdstat);
2431d65abd66SPyun YongHyeon if (txstat & RL_TDESC_STAT_OWN)
2432a94100faSBill Paul break;
2433a94100faSBill Paul /*
2434a94100faSBill Paul * We only stash mbufs in the last descriptor
2435a94100faSBill Paul * in a fragment chain, which also happens to
2436a94100faSBill Paul * be the only place where the TX status bits
2437a94100faSBill Paul * are valid.
2438a94100faSBill Paul */
2439a94100faSBill Paul if (txstat & RL_TDESC_CMD_EOF) {
2440d65abd66SPyun YongHyeon txd = &sc->rl_ldata.rl_tx_desc[cons];
2441d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag,
2442d65abd66SPyun YongHyeon txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
2443d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag,
2444d65abd66SPyun YongHyeon txd->tx_dmamap);
2445d65abd66SPyun YongHyeon KASSERT(txd->tx_m != NULL,
2446d65abd66SPyun YongHyeon ("%s: freeing NULL mbufs!", __func__));
2447d65abd66SPyun YongHyeon m_freem(txd->tx_m);
2448d65abd66SPyun YongHyeon txd->tx_m = NULL;
2449a94100faSBill Paul if (txstat & (RL_TDESC_STAT_EXCESSCOL|
2450a94100faSBill Paul RL_TDESC_STAT_COLCNT))
2451c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
2452a94100faSBill Paul if (txstat & RL_TDESC_STAT_TXERRSUM)
2453c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
2454a94100faSBill Paul else
2455c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
2456a94100faSBill Paul }
2457a94100faSBill Paul sc->rl_ldata.rl_tx_free++;
24584519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
2459a94100faSBill Paul }
2460d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_considx = cons;
2461a94100faSBill Paul
2462a94100faSBill Paul /* No changes made to the TX ring, so no flush needed */
2463a94100faSBill Paul
2464d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) {
2465ed510fb0SBill Paul #ifdef RE_TX_MODERATION
2466a94100faSBill Paul /*
2467b4b95879SMarius Strobl * If not all descriptors have been reaped yet, reload
2468b4b95879SMarius Strobl * the timer so that we will eventually get another
2469a94100faSBill Paul * interrupt that will cause us to re-enter this routine.
2470a94100faSBill Paul * This is done in case the transmitter has gone idle.
2471a94100faSBill Paul */
2472a94100faSBill Paul CSR_WRITE_4(sc, RL_TIMERCNT, 1);
2473ed510fb0SBill Paul #endif
2474b4b95879SMarius Strobl } else
2475b4b95879SMarius Strobl sc->rl_watchdog_timer = 0;
2476a94100faSBill Paul }
2477a94100faSBill Paul
2478a94100faSBill Paul static void
re_tick(void * xsc)24797b5ffebfSPyun YongHyeon re_tick(void *xsc)
2480a94100faSBill Paul {
2481a94100faSBill Paul struct rl_softc *sc;
2482d1754a9bSJohn Baldwin struct mii_data *mii;
2483a94100faSBill Paul
2484a94100faSBill Paul sc = xsc;
248597b9d4baSJohn-Mark Gurney
248697b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
248797b9d4baSJohn-Mark Gurney
24881d545c7aSMarius Strobl mii = device_get_softc(sc->rl_miibus);
2489a94100faSBill Paul mii_tick(mii);
24900fe200d9SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_LINK) == 0)
24910fe200d9SPyun YongHyeon re_miibus_statchg(sc->rl_dev);
2492c2d2e19cSPyun YongHyeon /*
2493c2d2e19cSPyun YongHyeon * Reclaim transmitted frames here. Technically it is not
2494c2d2e19cSPyun YongHyeon * necessary to do here but it ensures periodic reclamation
2495c2d2e19cSPyun YongHyeon * regardless of Tx completion interrupt which seems to be
2496c2d2e19cSPyun YongHyeon * lost on PCIe based controllers under certain situations.
2497c2d2e19cSPyun YongHyeon */
2498c2d2e19cSPyun YongHyeon re_txeof(sc);
2499130b6dfbSPyun YongHyeon re_watchdog(sc);
2500d1754a9bSJohn Baldwin callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
2501a94100faSBill Paul }
2502a94100faSBill Paul
2503a94100faSBill Paul #ifdef DEVICE_POLLING
25041abcdbd1SAttilio Rao static int
re_poll(if_t ifp,enum poll_cmd cmd,int count)25054519a073SJustin Hibbits re_poll(if_t ifp, enum poll_cmd cmd, int count)
2506a94100faSBill Paul {
25074519a073SJustin Hibbits struct rl_softc *sc = if_getsoftc(ifp);
25081abcdbd1SAttilio Rao int rx_npkts = 0;
2509a94100faSBill Paul
2510a94100faSBill Paul RL_LOCK(sc);
25114519a073SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
25121abcdbd1SAttilio Rao rx_npkts = re_poll_locked(ifp, cmd, count);
251397b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
25141abcdbd1SAttilio Rao return (rx_npkts);
251597b9d4baSJohn-Mark Gurney }
251697b9d4baSJohn-Mark Gurney
25171abcdbd1SAttilio Rao static int
re_poll_locked(if_t ifp,enum poll_cmd cmd,int count)25184519a073SJustin Hibbits re_poll_locked(if_t ifp, enum poll_cmd cmd, int count)
251997b9d4baSJohn-Mark Gurney {
25204519a073SJustin Hibbits struct rl_softc *sc = if_getsoftc(ifp);
25211abcdbd1SAttilio Rao int rx_npkts;
252297b9d4baSJohn-Mark Gurney
252397b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
252497b9d4baSJohn-Mark Gurney
2525a94100faSBill Paul sc->rxcycles = count;
25261abcdbd1SAttilio Rao re_rxeof(sc, &rx_npkts);
2527a94100faSBill Paul re_txeof(sc);
2528a94100faSBill Paul
25294519a073SJustin Hibbits if (!if_sendq_empty(ifp))
2530d180a66fSPyun YongHyeon re_start_locked(ifp);
2531a94100faSBill Paul
2532a94100faSBill Paul if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
2533a94100faSBill Paul u_int16_t status;
2534a94100faSBill Paul
2535a94100faSBill Paul status = CSR_READ_2(sc, RL_ISR);
2536a94100faSBill Paul if (status == 0xffff)
25371abcdbd1SAttilio Rao return (rx_npkts);
2538a94100faSBill Paul if (status)
2539a94100faSBill Paul CSR_WRITE_2(sc, RL_ISR, status);
2540818951afSPyun YongHyeon if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) &&
2541818951afSPyun YongHyeon (sc->rl_flags & RL_FLAG_PCIE))
2542818951afSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
2543a94100faSBill Paul
2544a94100faSBill Paul /*
2545a94100faSBill Paul * XXX check behaviour on receiver stalls.
2546a94100faSBill Paul */
2547a94100faSBill Paul
25488476c243SPyun YongHyeon if (status & RL_ISR_SYSTEM_ERR) {
25494519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
255097b9d4baSJohn-Mark Gurney re_init_locked(sc);
2551a94100faSBill Paul }
25528476c243SPyun YongHyeon }
25531abcdbd1SAttilio Rao return (rx_npkts);
2554a94100faSBill Paul }
2555a94100faSBill Paul #endif /* DEVICE_POLLING */
2556a94100faSBill Paul
2557ef544f63SPaolo Pisati static int
re_intr(void * arg)25587b5ffebfSPyun YongHyeon re_intr(void *arg)
2559a94100faSBill Paul {
2560a94100faSBill Paul struct rl_softc *sc;
2561ed510fb0SBill Paul uint16_t status;
2562a94100faSBill Paul
2563a94100faSBill Paul sc = arg;
2564ed510fb0SBill Paul
2565ed510fb0SBill Paul status = CSR_READ_2(sc, RL_ISR);
2566498bd0d3SBill Paul if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0)
2567ef544f63SPaolo Pisati return (FILTER_STRAY);
2568ed510fb0SBill Paul CSR_WRITE_2(sc, RL_IMR, 0);
2569ed510fb0SBill Paul
2570cbc4d2dbSJohn Baldwin taskqueue_enqueue(taskqueue_fast, &sc->rl_inttask);
2571ed510fb0SBill Paul
2572ef544f63SPaolo Pisati return (FILTER_HANDLED);
2573ed510fb0SBill Paul }
2574ed510fb0SBill Paul
2575ed510fb0SBill Paul static void
re_int_task(void * arg,int npending)25767b5ffebfSPyun YongHyeon re_int_task(void *arg, int npending)
2577ed510fb0SBill Paul {
2578ed510fb0SBill Paul struct rl_softc *sc;
25794519a073SJustin Hibbits if_t ifp;
2580ed510fb0SBill Paul u_int16_t status;
2581ed510fb0SBill Paul int rval = 0;
2582ed510fb0SBill Paul
2583ed510fb0SBill Paul sc = arg;
2584ed510fb0SBill Paul ifp = sc->rl_ifp;
2585a94100faSBill Paul
2586a94100faSBill Paul RL_LOCK(sc);
258797b9d4baSJohn-Mark Gurney
2588a94100faSBill Paul status = CSR_READ_2(sc, RL_ISR);
2589a94100faSBill Paul CSR_WRITE_2(sc, RL_ISR, status);
2590a94100faSBill Paul
2591d65abd66SPyun YongHyeon if (sc->suspended ||
25924519a073SJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
2593ed510fb0SBill Paul RL_UNLOCK(sc);
2594ed510fb0SBill Paul return;
2595ed510fb0SBill Paul }
2596a94100faSBill Paul
2597ed510fb0SBill Paul #ifdef DEVICE_POLLING
25984519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING) {
2599ed510fb0SBill Paul RL_UNLOCK(sc);
2600ed510fb0SBill Paul return;
2601ed510fb0SBill Paul }
2602ed510fb0SBill Paul #endif
2603a94100faSBill Paul
26046c3e93cbSGleb Smirnoff if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW))
26051abcdbd1SAttilio Rao rval = re_rxeof(sc, NULL);
2606ed510fb0SBill Paul
2607818951afSPyun YongHyeon /*
2608818951afSPyun YongHyeon * Some chips will ignore a second TX request issued
2609818951afSPyun YongHyeon * while an existing transmission is in progress. If
2610818951afSPyun YongHyeon * the transmitter goes idle but there are still
2611818951afSPyun YongHyeon * packets waiting to be sent, we need to restart the
2612818951afSPyun YongHyeon * channel here to flush them out. This only seems to
2613818951afSPyun YongHyeon * be required with the PCIe devices.
2614818951afSPyun YongHyeon */
2615818951afSPyun YongHyeon if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) &&
2616818951afSPyun YongHyeon (sc->rl_flags & RL_FLAG_PCIE))
2617818951afSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
26183d85c23dSPyun YongHyeon if (status & (
2619ed510fb0SBill Paul #ifdef RE_TX_MODERATION
26203d85c23dSPyun YongHyeon RL_ISR_TIMEOUT_EXPIRED|
2621ed510fb0SBill Paul #else
26223d85c23dSPyun YongHyeon RL_ISR_TX_OK|
2623ed510fb0SBill Paul #endif
2624ed510fb0SBill Paul RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL))
2625a94100faSBill Paul re_txeof(sc);
2626a94100faSBill Paul
26278476c243SPyun YongHyeon if (status & RL_ISR_SYSTEM_ERR) {
26284519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
262997b9d4baSJohn-Mark Gurney re_init_locked(sc);
26308476c243SPyun YongHyeon }
2631a94100faSBill Paul
26324519a073SJustin Hibbits if (!if_sendq_empty(ifp))
2633d180a66fSPyun YongHyeon re_start_locked(ifp);
2634a94100faSBill Paul
2635a94100faSBill Paul RL_UNLOCK(sc);
2636ed510fb0SBill Paul
2637ed510fb0SBill Paul if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) {
2638cbc4d2dbSJohn Baldwin taskqueue_enqueue(taskqueue_fast, &sc->rl_inttask);
2639ed510fb0SBill Paul return;
2640ed510fb0SBill Paul }
2641ed510fb0SBill Paul
2642ed510fb0SBill Paul CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS);
2643a94100faSBill Paul }
2644a94100faSBill Paul
2645502be0f7SPyun YongHyeon static void
re_intr_msi(void * xsc)2646502be0f7SPyun YongHyeon re_intr_msi(void *xsc)
2647502be0f7SPyun YongHyeon {
2648502be0f7SPyun YongHyeon struct rl_softc *sc;
26494519a073SJustin Hibbits if_t ifp;
2650502be0f7SPyun YongHyeon uint16_t intrs, status;
2651502be0f7SPyun YongHyeon
2652502be0f7SPyun YongHyeon sc = xsc;
2653502be0f7SPyun YongHyeon RL_LOCK(sc);
2654502be0f7SPyun YongHyeon
2655502be0f7SPyun YongHyeon ifp = sc->rl_ifp;
2656502be0f7SPyun YongHyeon #ifdef DEVICE_POLLING
26574519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING) {
2658502be0f7SPyun YongHyeon RL_UNLOCK(sc);
2659502be0f7SPyun YongHyeon return;
2660502be0f7SPyun YongHyeon }
2661502be0f7SPyun YongHyeon #endif
2662502be0f7SPyun YongHyeon /* Disable interrupts. */
2663502be0f7SPyun YongHyeon CSR_WRITE_2(sc, RL_IMR, 0);
26644519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
2665502be0f7SPyun YongHyeon RL_UNLOCK(sc);
2666502be0f7SPyun YongHyeon return;
2667502be0f7SPyun YongHyeon }
2668502be0f7SPyun YongHyeon
2669502be0f7SPyun YongHyeon intrs = RL_INTRS_CPLUS;
2670502be0f7SPyun YongHyeon status = CSR_READ_2(sc, RL_ISR);
2671502be0f7SPyun YongHyeon CSR_WRITE_2(sc, RL_ISR, status);
2672502be0f7SPyun YongHyeon if (sc->rl_int_rx_act > 0) {
2673502be0f7SPyun YongHyeon intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW |
2674502be0f7SPyun YongHyeon RL_ISR_RX_OVERRUN);
2675502be0f7SPyun YongHyeon status &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW |
2676502be0f7SPyun YongHyeon RL_ISR_RX_OVERRUN);
2677502be0f7SPyun YongHyeon }
2678502be0f7SPyun YongHyeon
2679502be0f7SPyun YongHyeon if (status & (RL_ISR_TIMEOUT_EXPIRED | RL_ISR_RX_OK | RL_ISR_RX_ERR |
2680502be0f7SPyun YongHyeon RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) {
2681502be0f7SPyun YongHyeon re_rxeof(sc, NULL);
26824519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
2683502be0f7SPyun YongHyeon if (sc->rl_int_rx_mod != 0 &&
2684502be0f7SPyun YongHyeon (status & (RL_ISR_RX_OK | RL_ISR_RX_ERR |
2685502be0f7SPyun YongHyeon RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) != 0) {
2686502be0f7SPyun YongHyeon /* Rearm one-shot timer. */
2687502be0f7SPyun YongHyeon CSR_WRITE_4(sc, RL_TIMERCNT, 1);
2688502be0f7SPyun YongHyeon intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR |
2689502be0f7SPyun YongHyeon RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN);
2690502be0f7SPyun YongHyeon sc->rl_int_rx_act = 1;
2691502be0f7SPyun YongHyeon } else {
2692502be0f7SPyun YongHyeon intrs |= RL_ISR_RX_OK | RL_ISR_RX_ERR |
2693502be0f7SPyun YongHyeon RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN;
2694502be0f7SPyun YongHyeon sc->rl_int_rx_act = 0;
2695502be0f7SPyun YongHyeon }
2696502be0f7SPyun YongHyeon }
2697502be0f7SPyun YongHyeon }
2698502be0f7SPyun YongHyeon
2699502be0f7SPyun YongHyeon /*
2700502be0f7SPyun YongHyeon * Some chips will ignore a second TX request issued
2701502be0f7SPyun YongHyeon * while an existing transmission is in progress. If
2702502be0f7SPyun YongHyeon * the transmitter goes idle but there are still
2703502be0f7SPyun YongHyeon * packets waiting to be sent, we need to restart the
2704502be0f7SPyun YongHyeon * channel here to flush them out. This only seems to
2705502be0f7SPyun YongHyeon * be required with the PCIe devices.
2706502be0f7SPyun YongHyeon */
2707502be0f7SPyun YongHyeon if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) &&
2708502be0f7SPyun YongHyeon (sc->rl_flags & RL_FLAG_PCIE))
2709502be0f7SPyun YongHyeon CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
2710502be0f7SPyun YongHyeon if (status & (RL_ISR_TX_OK | RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL))
2711502be0f7SPyun YongHyeon re_txeof(sc);
2712502be0f7SPyun YongHyeon
2713502be0f7SPyun YongHyeon if (status & RL_ISR_SYSTEM_ERR) {
27144519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
2715502be0f7SPyun YongHyeon re_init_locked(sc);
2716502be0f7SPyun YongHyeon }
2717502be0f7SPyun YongHyeon
27184519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
27194519a073SJustin Hibbits if (!if_sendq_empty(ifp))
2720502be0f7SPyun YongHyeon re_start_locked(ifp);
2721502be0f7SPyun YongHyeon CSR_WRITE_2(sc, RL_IMR, intrs);
2722502be0f7SPyun YongHyeon }
2723502be0f7SPyun YongHyeon RL_UNLOCK(sc);
2724502be0f7SPyun YongHyeon }
2725502be0f7SPyun YongHyeon
2726d65abd66SPyun YongHyeon static int
re_encap(struct rl_softc * sc,struct mbuf ** m_head)27277b5ffebfSPyun YongHyeon re_encap(struct rl_softc *sc, struct mbuf **m_head)
2728d65abd66SPyun YongHyeon {
2729d65abd66SPyun YongHyeon struct rl_txdesc *txd, *txd_last;
2730d65abd66SPyun YongHyeon bus_dma_segment_t segs[RL_NTXSEGS];
2731d65abd66SPyun YongHyeon bus_dmamap_t map;
2732d65abd66SPyun YongHyeon struct mbuf *m_new;
2733d65abd66SPyun YongHyeon struct rl_desc *desc;
2734d65abd66SPyun YongHyeon int nsegs, prod;
2735d65abd66SPyun YongHyeon int i, error, ei, si;
2736d65abd66SPyun YongHyeon int padlen;
2737ccf34c81SPyun YongHyeon uint32_t cmdstat, csum_flags, vlanctl;
2738a94100faSBill Paul
2739d65abd66SPyun YongHyeon RL_LOCK_ASSERT(sc);
2740738489d1SPyun YongHyeon M_ASSERTPKTHDR((*m_head));
27410fc4974fSBill Paul
27420fc4974fSBill Paul /*
27430fc4974fSBill Paul * With some of the RealTek chips, using the checksum offload
27440fc4974fSBill Paul * support in conjunction with the autopadding feature results
27450fc4974fSBill Paul * in the transmission of corrupt frames. For example, if we
27460fc4974fSBill Paul * need to send a really small IP fragment that's less than 60
27470fc4974fSBill Paul * bytes in size, and IP header checksumming is enabled, the
27480fc4974fSBill Paul * resulting ethernet frame that appears on the wire will
274999c8ae87SPyun YongHyeon * have garbled payload. To work around this, if TX IP checksum
27500fc4974fSBill Paul * offload is enabled, we always manually pad short frames out
2751d65abd66SPyun YongHyeon * to the minimum ethernet frame size.
27520fc4974fSBill Paul */
2753f2e491c9SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_AUTOPAD) == 0 &&
2754deb5c680SPyun YongHyeon (*m_head)->m_pkthdr.len < RL_IP4CSUMTX_PADLEN &&
275599c8ae87SPyun YongHyeon ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) != 0) {
2756d65abd66SPyun YongHyeon padlen = RL_MIN_FRAMELEN - (*m_head)->m_pkthdr.len;
2757d65abd66SPyun YongHyeon if (M_WRITABLE(*m_head) == 0) {
2758d65abd66SPyun YongHyeon /* Get a writable copy. */
2759c6499eccSGleb Smirnoff m_new = m_dup(*m_head, M_NOWAIT);
2760d65abd66SPyun YongHyeon m_freem(*m_head);
2761d65abd66SPyun YongHyeon if (m_new == NULL) {
2762d65abd66SPyun YongHyeon *m_head = NULL;
2763a94100faSBill Paul return (ENOBUFS);
2764a94100faSBill Paul }
2765d65abd66SPyun YongHyeon *m_head = m_new;
2766d65abd66SPyun YongHyeon }
2767d65abd66SPyun YongHyeon if ((*m_head)->m_next != NULL ||
2768d65abd66SPyun YongHyeon M_TRAILINGSPACE(*m_head) < padlen) {
2769c6499eccSGleb Smirnoff m_new = m_defrag(*m_head, M_NOWAIT);
2770b4b95879SMarius Strobl if (m_new == NULL) {
2771b4b95879SMarius Strobl m_freem(*m_head);
2772b4b95879SMarius Strobl *m_head = NULL;
277380a2a305SJohn-Mark Gurney return (ENOBUFS);
2774b4b95879SMarius Strobl }
2775d65abd66SPyun YongHyeon } else
2776d65abd66SPyun YongHyeon m_new = *m_head;
2777a94100faSBill Paul
27780fc4974fSBill Paul /*
27790fc4974fSBill Paul * Manually pad short frames, and zero the pad space
27800fc4974fSBill Paul * to avoid leaking data.
27810fc4974fSBill Paul */
2782d65abd66SPyun YongHyeon bzero(mtod(m_new, char *) + m_new->m_pkthdr.len, padlen);
2783d65abd66SPyun YongHyeon m_new->m_pkthdr.len += padlen;
27840fc4974fSBill Paul m_new->m_len = m_new->m_pkthdr.len;
2785d65abd66SPyun YongHyeon *m_head = m_new;
27860fc4974fSBill Paul }
27870fc4974fSBill Paul
2788d65abd66SPyun YongHyeon prod = sc->rl_ldata.rl_tx_prodidx;
2789d65abd66SPyun YongHyeon txd = &sc->rl_ldata.rl_tx_desc[prod];
2790d65abd66SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap,
2791d65abd66SPyun YongHyeon *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
2792d65abd66SPyun YongHyeon if (error == EFBIG) {
2793c6499eccSGleb Smirnoff m_new = m_collapse(*m_head, M_NOWAIT, RL_NTXSEGS);
2794d65abd66SPyun YongHyeon if (m_new == NULL) {
2795d65abd66SPyun YongHyeon m_freem(*m_head);
2796b4b95879SMarius Strobl *m_head = NULL;
2797d65abd66SPyun YongHyeon return (ENOBUFS);
2798a94100faSBill Paul }
2799d65abd66SPyun YongHyeon *m_head = m_new;
2800d65abd66SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag,
2801d65abd66SPyun YongHyeon txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
2802d65abd66SPyun YongHyeon if (error != 0) {
2803d65abd66SPyun YongHyeon m_freem(*m_head);
2804d65abd66SPyun YongHyeon *m_head = NULL;
2805d65abd66SPyun YongHyeon return (error);
2806a94100faSBill Paul }
2807d65abd66SPyun YongHyeon } else if (error != 0)
2808d65abd66SPyun YongHyeon return (error);
2809d65abd66SPyun YongHyeon if (nsegs == 0) {
2810d65abd66SPyun YongHyeon m_freem(*m_head);
2811d65abd66SPyun YongHyeon *m_head = NULL;
2812d65abd66SPyun YongHyeon return (EIO);
2813d65abd66SPyun YongHyeon }
2814d65abd66SPyun YongHyeon
2815d65abd66SPyun YongHyeon /* Check for number of available descriptors. */
2816d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_tx_free - nsegs <= 1) {
2817d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap);
2818d65abd66SPyun YongHyeon return (ENOBUFS);
2819d65abd66SPyun YongHyeon }
2820d65abd66SPyun YongHyeon
2821d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap,
2822d65abd66SPyun YongHyeon BUS_DMASYNC_PREWRITE);
2823a94100faSBill Paul
2824a94100faSBill Paul /*
2825d65abd66SPyun YongHyeon * Set up checksum offload. Note: checksum offload bits must
2826d65abd66SPyun YongHyeon * appear in all descriptors of a multi-descriptor transmit
2827d65abd66SPyun YongHyeon * attempt. This is according to testing done with an 8169
2828d65abd66SPyun YongHyeon * chip. This is a requirement.
2829a94100faSBill Paul */
2830deb5c680SPyun YongHyeon vlanctl = 0;
2831d65abd66SPyun YongHyeon csum_flags = 0;
2832d6d7d923SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
2833d6d7d923SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_DESCV2) != 0) {
2834d6d7d923SPyun YongHyeon csum_flags |= RL_TDESC_CMD_LGSEND;
2835d6d7d923SPyun YongHyeon vlanctl |= ((uint32_t)(*m_head)->m_pkthdr.tso_segsz <<
2836d6d7d923SPyun YongHyeon RL_TDESC_CMD_MSSVALV2_SHIFT);
2837d6d7d923SPyun YongHyeon } else {
2838d6d7d923SPyun YongHyeon csum_flags |= RL_TDESC_CMD_LGSEND |
2839d65abd66SPyun YongHyeon ((uint32_t)(*m_head)->m_pkthdr.tso_segsz <<
2840d65abd66SPyun YongHyeon RL_TDESC_CMD_MSSVAL_SHIFT);
2841d6d7d923SPyun YongHyeon }
2842d6d7d923SPyun YongHyeon } else {
284399c8ae87SPyun YongHyeon /*
284499c8ae87SPyun YongHyeon * Unconditionally enable IP checksum if TCP or UDP
284599c8ae87SPyun YongHyeon * checksum is required. Otherwise, TCP/UDP checksum
28462df05392SSergey Kandaurov * doesn't make effects.
284799c8ae87SPyun YongHyeon */
284899c8ae87SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) {
2849deb5c680SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
2850d65abd66SPyun YongHyeon csum_flags |= RL_TDESC_CMD_IPCSUM;
2851deb5c680SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags &
2852deb5c680SPyun YongHyeon CSUM_TCP) != 0)
2853d65abd66SPyun YongHyeon csum_flags |= RL_TDESC_CMD_TCPCSUM;
2854deb5c680SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags &
2855deb5c680SPyun YongHyeon CSUM_UDP) != 0)
2856d65abd66SPyun YongHyeon csum_flags |= RL_TDESC_CMD_UDPCSUM;
2857deb5c680SPyun YongHyeon } else {
2858deb5c680SPyun YongHyeon vlanctl |= RL_TDESC_CMD_IPCSUMV2;
2859deb5c680SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags &
2860deb5c680SPyun YongHyeon CSUM_TCP) != 0)
2861deb5c680SPyun YongHyeon vlanctl |= RL_TDESC_CMD_TCPCSUMV2;
2862deb5c680SPyun YongHyeon if (((*m_head)->m_pkthdr.csum_flags &
2863deb5c680SPyun YongHyeon CSUM_UDP) != 0)
2864deb5c680SPyun YongHyeon vlanctl |= RL_TDESC_CMD_UDPCSUMV2;
2865deb5c680SPyun YongHyeon }
2866d65abd66SPyun YongHyeon }
286799c8ae87SPyun YongHyeon }
2868a94100faSBill Paul
2869ccf34c81SPyun YongHyeon /*
2870ccf34c81SPyun YongHyeon * Set up hardware VLAN tagging. Note: vlan tag info must
2871ccf34c81SPyun YongHyeon * appear in all descriptors of a multi-descriptor
2872ccf34c81SPyun YongHyeon * transmission attempt.
2873ccf34c81SPyun YongHyeon */
2874ccf34c81SPyun YongHyeon if ((*m_head)->m_flags & M_VLANTAG)
2875bddff934SPyun YongHyeon vlanctl |= bswap16((*m_head)->m_pkthdr.ether_vtag) |
2876deb5c680SPyun YongHyeon RL_TDESC_VLANCTL_TAG;
2877ccf34c81SPyun YongHyeon
2878d65abd66SPyun YongHyeon si = prod;
2879d65abd66SPyun YongHyeon for (i = 0; i < nsegs; i++, prod = RL_TX_DESC_NXT(sc, prod)) {
2880d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_tx_list[prod];
2881deb5c680SPyun YongHyeon desc->rl_vlanctl = htole32(vlanctl);
2882d65abd66SPyun YongHyeon desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr));
2883d65abd66SPyun YongHyeon desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr));
2884d65abd66SPyun YongHyeon cmdstat = segs[i].ds_len;
2885d65abd66SPyun YongHyeon if (i != 0)
2886d65abd66SPyun YongHyeon cmdstat |= RL_TDESC_CMD_OWN;
2887d65abd66SPyun YongHyeon if (prod == sc->rl_ldata.rl_tx_desc_cnt - 1)
2888d65abd66SPyun YongHyeon cmdstat |= RL_TDESC_CMD_EOR;
2889d65abd66SPyun YongHyeon desc->rl_cmdstat = htole32(cmdstat | csum_flags);
2890d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_free--;
2891d65abd66SPyun YongHyeon }
2892d65abd66SPyun YongHyeon /* Update producer index. */
2893d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_prodidx = prod;
2894a94100faSBill Paul
2895d65abd66SPyun YongHyeon /* Set EOF on the last descriptor. */
2896d65abd66SPyun YongHyeon ei = RL_TX_DESC_PRV(sc, prod);
2897d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_tx_list[ei];
2898d65abd66SPyun YongHyeon desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF);
2899d65abd66SPyun YongHyeon
2900d65abd66SPyun YongHyeon desc = &sc->rl_ldata.rl_tx_list[si];
2901d65abd66SPyun YongHyeon /* Set SOF and transfer ownership of packet to the chip. */
2902d65abd66SPyun YongHyeon desc->rl_cmdstat |= htole32(RL_TDESC_CMD_OWN | RL_TDESC_CMD_SOF);
2903a94100faSBill Paul
2904d65abd66SPyun YongHyeon /*
2905d65abd66SPyun YongHyeon * Insure that the map for this transmission
2906d65abd66SPyun YongHyeon * is placed at the array index of the last descriptor
2907d65abd66SPyun YongHyeon * in this chain. (Swap last and first dmamaps.)
2908d65abd66SPyun YongHyeon */
2909d65abd66SPyun YongHyeon txd_last = &sc->rl_ldata.rl_tx_desc[ei];
2910d65abd66SPyun YongHyeon map = txd->tx_dmamap;
2911d65abd66SPyun YongHyeon txd->tx_dmamap = txd_last->tx_dmamap;
2912d65abd66SPyun YongHyeon txd_last->tx_dmamap = map;
2913d65abd66SPyun YongHyeon txd_last->tx_m = *m_head;
2914a94100faSBill Paul
2915a94100faSBill Paul return (0);
2916a94100faSBill Paul }
2917a94100faSBill Paul
291897b9d4baSJohn-Mark Gurney static void
re_start(if_t ifp)29194519a073SJustin Hibbits re_start(if_t ifp)
292097b9d4baSJohn-Mark Gurney {
2921d180a66fSPyun YongHyeon struct rl_softc *sc;
292297b9d4baSJohn-Mark Gurney
29234519a073SJustin Hibbits sc = if_getsoftc(ifp);
2924d180a66fSPyun YongHyeon RL_LOCK(sc);
2925d180a66fSPyun YongHyeon re_start_locked(ifp);
2926d180a66fSPyun YongHyeon RL_UNLOCK(sc);
292797b9d4baSJohn-Mark Gurney }
292897b9d4baSJohn-Mark Gurney
2929a94100faSBill Paul /*
2930a94100faSBill Paul * Main transmit routine for C+ and gigE NICs.
2931a94100faSBill Paul */
2932a94100faSBill Paul static void
re_start_locked(if_t ifp)29334519a073SJustin Hibbits re_start_locked(if_t ifp)
2934a94100faSBill Paul {
2935a94100faSBill Paul struct rl_softc *sc;
2936d65abd66SPyun YongHyeon struct mbuf *m_head;
2937d65abd66SPyun YongHyeon int queued;
2938a94100faSBill Paul
29394519a073SJustin Hibbits sc = if_getsoftc(ifp);
294097b9d4baSJohn-Mark Gurney
2941579a6e3cSLuigi Rizzo #ifdef DEV_NETMAP
2942579a6e3cSLuigi Rizzo /* XXX is this necessary ? */
29434519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_NETMAP) {
29442ff91c17SVincenzo Maffione struct netmap_kring *kring = NA(ifp)->tx_rings[0];
2945579a6e3cSLuigi Rizzo if (sc->rl_ldata.rl_tx_prodidx != kring->nr_hwcur) {
2946579a6e3cSLuigi Rizzo /* kick the tx unit */
2947579a6e3cSLuigi Rizzo CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
2948579a6e3cSLuigi Rizzo #ifdef RE_TX_MODERATION
2949579a6e3cSLuigi Rizzo CSR_WRITE_4(sc, RL_TIMERCNT, 1);
2950579a6e3cSLuigi Rizzo #endif
2951579a6e3cSLuigi Rizzo sc->rl_watchdog_timer = 5;
2952579a6e3cSLuigi Rizzo }
2953579a6e3cSLuigi Rizzo return;
2954579a6e3cSLuigi Rizzo }
2955579a6e3cSLuigi Rizzo #endif /* DEV_NETMAP */
2956e9f8886eSMarius Strobl
29574519a073SJustin Hibbits if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2958d180a66fSPyun YongHyeon IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0)
2959ed510fb0SBill Paul return;
2960a94100faSBill Paul
29614519a073SJustin Hibbits for (queued = 0; !if_sendq_empty(ifp) &&
2962d65abd66SPyun YongHyeon sc->rl_ldata.rl_tx_free > 1;) {
29634519a073SJustin Hibbits m_head = if_dequeue(ifp);
2964a94100faSBill Paul if (m_head == NULL)
2965a94100faSBill Paul break;
2966a94100faSBill Paul
2967d65abd66SPyun YongHyeon if (re_encap(sc, &m_head) != 0) {
2968b4b95879SMarius Strobl if (m_head == NULL)
2969b4b95879SMarius Strobl break;
29704519a073SJustin Hibbits if_sendq_prepend(ifp, m_head);
29714519a073SJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
2972a94100faSBill Paul break;
2973a94100faSBill Paul }
2974a94100faSBill Paul
2975a94100faSBill Paul /*
2976a94100faSBill Paul * If there's a BPF listener, bounce a copy of this frame
2977a94100faSBill Paul * to him.
2978a94100faSBill Paul */
297959a0d28bSChristian S.J. Peron ETHER_BPF_MTAP(ifp, m_head);
298052732175SMax Laier
298152732175SMax Laier queued++;
2982a94100faSBill Paul }
2983a94100faSBill Paul
2984ed510fb0SBill Paul if (queued == 0) {
2985ed510fb0SBill Paul #ifdef RE_TX_MODERATION
2986d65abd66SPyun YongHyeon if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt)
2987ed510fb0SBill Paul CSR_WRITE_4(sc, RL_TIMERCNT, 1);
2988ed510fb0SBill Paul #endif
298952732175SMax Laier return;
2990ed510fb0SBill Paul }
299152732175SMax Laier
2992306c97e2SMark Johnston re_start_tx(sc);
2993306c97e2SMark Johnston }
2994a94100faSBill Paul
2995306c97e2SMark Johnston static void
re_start_tx(struct rl_softc * sc)2996306c97e2SMark Johnston re_start_tx(struct rl_softc *sc)
2997306c97e2SMark Johnston {
2998306c97e2SMark Johnston
2999306c97e2SMark Johnston /* Flush the TX descriptors */
3000a94100faSBill Paul bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
3001a94100faSBill Paul sc->rl_ldata.rl_tx_list_map,
3002a94100faSBill Paul BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
3003a94100faSBill Paul
30040fc4974fSBill Paul CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START);
3005a94100faSBill Paul
3006ed510fb0SBill Paul #ifdef RE_TX_MODERATION
3007a94100faSBill Paul /*
3008a94100faSBill Paul * Use the countdown timer for interrupt moderation.
3009a94100faSBill Paul * 'TX done' interrupts are disabled. Instead, we reset the
3010a94100faSBill Paul * countdown timer, which will begin counting until it hits
3011a94100faSBill Paul * the value in the TIMERINT register, and then trigger an
3012a94100faSBill Paul * interrupt. Each time we write to the TIMERCNT register,
3013a94100faSBill Paul * the timer count is reset to 0.
3014a94100faSBill Paul */
3015a94100faSBill Paul CSR_WRITE_4(sc, RL_TIMERCNT, 1);
3016ed510fb0SBill Paul #endif
3017a94100faSBill Paul
3018a94100faSBill Paul /*
3019a94100faSBill Paul * Set a timeout in case the chip goes out to lunch.
3020a94100faSBill Paul */
30211d545c7aSMarius Strobl sc->rl_watchdog_timer = 5;
3022a94100faSBill Paul }
3023a94100faSBill Paul
3024a94100faSBill Paul static void
re_set_jumbo(struct rl_softc * sc,int jumbo)302581eee0ebSPyun YongHyeon re_set_jumbo(struct rl_softc *sc, int jumbo)
302681eee0ebSPyun YongHyeon {
302781eee0ebSPyun YongHyeon
302881eee0ebSPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8168E_VL) {
302981eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 4096);
303081eee0ebSPyun YongHyeon return;
303181eee0ebSPyun YongHyeon }
303281eee0ebSPyun YongHyeon
303381eee0ebSPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
303481eee0ebSPyun YongHyeon if (jumbo != 0) {
3035e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg3, CSR_READ_1(sc, sc->rl_cfg3) |
303681eee0ebSPyun YongHyeon RL_CFG3_JUMBO_EN0);
303781eee0ebSPyun YongHyeon switch (sc->rl_hwrev->rl_rev) {
303881eee0ebSPyun YongHyeon case RL_HWREV_8168DP:
303981eee0ebSPyun YongHyeon break;
304081eee0ebSPyun YongHyeon case RL_HWREV_8168E:
3041e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg4,
3042e7e7593cSPyun YongHyeon CSR_READ_1(sc, sc->rl_cfg4) | 0x01);
304381eee0ebSPyun YongHyeon break;
304481eee0ebSPyun YongHyeon default:
3045e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg4,
3046e7e7593cSPyun YongHyeon CSR_READ_1(sc, sc->rl_cfg4) | RL_CFG4_JUMBO_EN1);
304781eee0ebSPyun YongHyeon }
304881eee0ebSPyun YongHyeon } else {
3049e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg3, CSR_READ_1(sc, sc->rl_cfg3) &
305081eee0ebSPyun YongHyeon ~RL_CFG3_JUMBO_EN0);
305181eee0ebSPyun YongHyeon switch (sc->rl_hwrev->rl_rev) {
305281eee0ebSPyun YongHyeon case RL_HWREV_8168DP:
305381eee0ebSPyun YongHyeon break;
305481eee0ebSPyun YongHyeon case RL_HWREV_8168E:
3055e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg4,
3056e7e7593cSPyun YongHyeon CSR_READ_1(sc, sc->rl_cfg4) & ~0x01);
305781eee0ebSPyun YongHyeon break;
305881eee0ebSPyun YongHyeon default:
3059e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg4,
3060e7e7593cSPyun YongHyeon CSR_READ_1(sc, sc->rl_cfg4) & ~RL_CFG4_JUMBO_EN1);
306181eee0ebSPyun YongHyeon }
306281eee0ebSPyun YongHyeon }
306381eee0ebSPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
306481eee0ebSPyun YongHyeon
306581eee0ebSPyun YongHyeon switch (sc->rl_hwrev->rl_rev) {
306681eee0ebSPyun YongHyeon case RL_HWREV_8168DP:
306781eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 4096);
306881eee0ebSPyun YongHyeon break;
306981eee0ebSPyun YongHyeon default:
307081eee0ebSPyun YongHyeon if (jumbo != 0)
307181eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 512);
307281eee0ebSPyun YongHyeon else
307381eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 4096);
307481eee0ebSPyun YongHyeon }
307581eee0ebSPyun YongHyeon }
307681eee0ebSPyun YongHyeon
307781eee0ebSPyun YongHyeon static void
re_init(void * xsc)30787b5ffebfSPyun YongHyeon re_init(void *xsc)
3079a94100faSBill Paul {
3080a94100faSBill Paul struct rl_softc *sc = xsc;
308197b9d4baSJohn-Mark Gurney
308297b9d4baSJohn-Mark Gurney RL_LOCK(sc);
308397b9d4baSJohn-Mark Gurney re_init_locked(sc);
308497b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
308597b9d4baSJohn-Mark Gurney }
308697b9d4baSJohn-Mark Gurney
308797b9d4baSJohn-Mark Gurney static void
re_init_locked(struct rl_softc * sc)30887b5ffebfSPyun YongHyeon re_init_locked(struct rl_softc *sc)
308997b9d4baSJohn-Mark Gurney {
30904519a073SJustin Hibbits if_t ifp = sc->rl_ifp;
3091a94100faSBill Paul struct mii_data *mii;
3092566ca8caSJung-uk Kim uint32_t reg;
309370acaecfSPyun YongHyeon uint16_t cfg;
3094a34d3ca6SJessica Clarke uint32_t idr[2];
3095a94100faSBill Paul
309697b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
309797b9d4baSJohn-Mark Gurney
3098a94100faSBill Paul mii = device_get_softc(sc->rl_miibus);
3099a94100faSBill Paul
31004519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
31018476c243SPyun YongHyeon return;
31028476c243SPyun YongHyeon
3103a94100faSBill Paul /*
3104a94100faSBill Paul * Cancel pending I/O and free all RX/TX buffers.
3105a94100faSBill Paul */
3106a94100faSBill Paul re_stop(sc);
3107a94100faSBill Paul
3108b659f1f0SPyun YongHyeon /* Put controller into known state. */
3109b659f1f0SPyun YongHyeon re_reset(sc);
3110b659f1f0SPyun YongHyeon
3111a94100faSBill Paul /*
31124a814a5eSPyun YongHyeon * For C+ mode, initialize the RX descriptors and mbufs.
31134a814a5eSPyun YongHyeon */
311481eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
31154519a073SJustin Hibbits if (if_getmtu(ifp) > RL_MTU) {
311681eee0ebSPyun YongHyeon if (re_jrx_list_init(sc) != 0) {
311781eee0ebSPyun YongHyeon device_printf(sc->rl_dev,
311881eee0ebSPyun YongHyeon "no memory for jumbo RX buffers\n");
311981eee0ebSPyun YongHyeon re_stop(sc);
312081eee0ebSPyun YongHyeon return;
312181eee0ebSPyun YongHyeon }
312281eee0ebSPyun YongHyeon /* Disable checksum offloading for jumbo frames. */
31234519a073SJustin Hibbits if_setcapenablebit(ifp, 0, (IFCAP_HWCSUM | IFCAP_TSO4));
31244519a073SJustin Hibbits if_sethwassistbits(ifp, 0, (RE_CSUM_FEATURES | CSUM_TSO));
312581eee0ebSPyun YongHyeon } else {
312681eee0ebSPyun YongHyeon if (re_rx_list_init(sc) != 0) {
312781eee0ebSPyun YongHyeon device_printf(sc->rl_dev,
312881eee0ebSPyun YongHyeon "no memory for RX buffers\n");
312981eee0ebSPyun YongHyeon re_stop(sc);
313081eee0ebSPyun YongHyeon return;
313181eee0ebSPyun YongHyeon }
313281eee0ebSPyun YongHyeon }
31334519a073SJustin Hibbits re_set_jumbo(sc, if_getmtu(ifp) > RL_MTU);
313481eee0ebSPyun YongHyeon } else {
31354a814a5eSPyun YongHyeon if (re_rx_list_init(sc) != 0) {
31364a814a5eSPyun YongHyeon device_printf(sc->rl_dev, "no memory for RX buffers\n");
31374a814a5eSPyun YongHyeon re_stop(sc);
31384a814a5eSPyun YongHyeon return;
31394a814a5eSPyun YongHyeon }
314081eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_PCIE) != 0 &&
314181eee0ebSPyun YongHyeon pci_get_device(sc->rl_dev) != RT_DEVICEID_8101E) {
31424519a073SJustin Hibbits if (if_getmtu(ifp) > RL_MTU)
314381eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 512);
314481eee0ebSPyun YongHyeon else
314581eee0ebSPyun YongHyeon pci_set_max_read_req(sc->rl_dev, 4096);
314681eee0ebSPyun YongHyeon }
314781eee0ebSPyun YongHyeon }
31484a814a5eSPyun YongHyeon re_tx_list_init(sc);
31494a814a5eSPyun YongHyeon
31504a814a5eSPyun YongHyeon /*
3151c2c6548bSBill Paul * Enable C+ RX and TX mode, as well as VLAN stripping and
3152edd03374SBill Paul * RX checksum offload. We must configure the C+ register
3153c2c6548bSBill Paul * before all others.
3154c2c6548bSBill Paul */
315570acaecfSPyun YongHyeon cfg = RL_CPLUSCMD_PCI_MRW;
31564519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0)
315770acaecfSPyun YongHyeon cfg |= RL_CPLUSCMD_RXCSUM_ENB;
31584519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) != 0)
315970acaecfSPyun YongHyeon cfg |= RL_CPLUSCMD_VLANSTRIP;
3160deb5c680SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MACSTAT) != 0) {
3161deb5c680SPyun YongHyeon cfg |= RL_CPLUSCMD_MACSTAT_DIS;
3162deb5c680SPyun YongHyeon /* XXX magic. */
3163deb5c680SPyun YongHyeon cfg |= 0x0001;
3164deb5c680SPyun YongHyeon } else
3165deb5c680SPyun YongHyeon cfg |= RL_CPLUSCMD_RXENB | RL_CPLUSCMD_TXENB;
3166deb5c680SPyun YongHyeon CSR_WRITE_2(sc, RL_CPLUS_CMD, cfg);
316781eee0ebSPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SC ||
316881eee0ebSPyun YongHyeon sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SCE) {
3169566ca8caSJung-uk Kim reg = 0x000fff00;
3170e7e7593cSPyun YongHyeon if ((CSR_READ_1(sc, sc->rl_cfg2) & RL_CFG2_PCI66MHZ) != 0)
3171566ca8caSJung-uk Kim reg |= 0x000000ff;
317281eee0ebSPyun YongHyeon if (sc->rl_hwrev->rl_rev == RL_HWREV_8169_8110SCE)
3173566ca8caSJung-uk Kim reg |= 0x00f00000;
3174566ca8caSJung-uk Kim CSR_WRITE_4(sc, 0x7c, reg);
3175566ca8caSJung-uk Kim /* Disable interrupt mitigation. */
3176566ca8caSJung-uk Kim CSR_WRITE_2(sc, 0xe2, 0);
3177566ca8caSJung-uk Kim }
3178ae644087SPyun YongHyeon /*
3179ae644087SPyun YongHyeon * Disable TSO if interface MTU size is greater than MSS
3180ae644087SPyun YongHyeon * allowed in controller.
3181ae644087SPyun YongHyeon */
31824519a073SJustin Hibbits if (if_getmtu(ifp) > RL_TSO_MTU && (if_getcapenable(ifp) & IFCAP_TSO4) != 0) {
31834519a073SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_TSO4);
31844519a073SJustin Hibbits if_sethwassistbits(ifp, 0, CSUM_TSO);
3185ae644087SPyun YongHyeon }
3186c2c6548bSBill Paul
3187c2c6548bSBill Paul /*
3188a94100faSBill Paul * Init our MAC address. Even though the chipset
3189a94100faSBill Paul * documentation doesn't mention it, we need to enter "Config
3190a94100faSBill Paul * register write enable" mode to modify the ID registers.
3191a94100faSBill Paul */
31924d3d7085SBernd Walter /* Copy MAC address on stack to align. */
3193a34d3ca6SJessica Clarke bzero(idr, sizeof(idr));
31944519a073SJustin Hibbits bcopy(if_getlladdr(ifp), idr, ETHER_ADDR_LEN);
3195a94100faSBill Paul CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
3196a34d3ca6SJessica Clarke CSR_WRITE_4(sc, RL_IDR0, htole32(idr[0]));
3197a34d3ca6SJessica Clarke CSR_WRITE_4(sc, RL_IDR4, htole32(idr[1]));
3198a94100faSBill Paul CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
3199a94100faSBill Paul
3200a94100faSBill Paul /*
3201d01fac16SPyun YongHyeon * Load the addresses of the RX and TX lists into the chip.
3202d01fac16SPyun YongHyeon */
3203d01fac16SPyun YongHyeon
3204d01fac16SPyun YongHyeon CSR_WRITE_4(sc, RL_RXLIST_ADDR_HI,
3205d01fac16SPyun YongHyeon RL_ADDR_HI(sc->rl_ldata.rl_rx_list_addr));
3206d01fac16SPyun YongHyeon CSR_WRITE_4(sc, RL_RXLIST_ADDR_LO,
3207d01fac16SPyun YongHyeon RL_ADDR_LO(sc->rl_ldata.rl_rx_list_addr));
3208d01fac16SPyun YongHyeon
3209d01fac16SPyun YongHyeon CSR_WRITE_4(sc, RL_TXLIST_ADDR_HI,
3210d01fac16SPyun YongHyeon RL_ADDR_HI(sc->rl_ldata.rl_tx_list_addr));
3211d01fac16SPyun YongHyeon CSR_WRITE_4(sc, RL_TXLIST_ADDR_LO,
3212d01fac16SPyun YongHyeon RL_ADDR_LO(sc->rl_ldata.rl_tx_list_addr));
3213d01fac16SPyun YongHyeon
321414013280SMarius Strobl if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) {
321514013280SMarius Strobl /* Disable RXDV gate. */
3216f1a5f291SMarius Strobl CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) &
3217f1a5f291SMarius Strobl ~0x00080000);
321814013280SMarius Strobl }
321914013280SMarius Strobl
322014013280SMarius Strobl /*
322114013280SMarius Strobl * Enable transmit and receive for pre-RTL8168G controllers.
322214013280SMarius Strobl * RX/TX MACs should be enabled before RX/TX configuration.
322314013280SMarius Strobl */
322414013280SMarius Strobl if ((sc->rl_flags & RL_FLAG_8168G_PLUS) == 0)
322514013280SMarius Strobl CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB | RL_CMD_RX_ENB);
3226f1a5f291SMarius Strobl
3227d01fac16SPyun YongHyeon /*
3228ff191365SJung-uk Kim * Set the initial TX configuration.
3229a94100faSBill Paul */
3230abc8ff44SBill Paul if (sc->rl_testmode) {
3231abc8ff44SBill Paul if (sc->rl_type == RL_8169)
3232abc8ff44SBill Paul CSR_WRITE_4(sc, RL_TXCFG,
3233abc8ff44SBill Paul RL_TXCFG_CONFIG|RL_LOOPTEST_ON);
3234a94100faSBill Paul else
3235abc8ff44SBill Paul CSR_WRITE_4(sc, RL_TXCFG,
3236abc8ff44SBill Paul RL_TXCFG_CONFIG|RL_LOOPTEST_ON_CPLUS);
3237abc8ff44SBill Paul } else
3238a94100faSBill Paul CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG);
3239d01fac16SPyun YongHyeon
3240d01fac16SPyun YongHyeon CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16);
3241d01fac16SPyun YongHyeon
3242a94100faSBill Paul /*
3243ff191365SJung-uk Kim * Set the initial RX configuration.
3244a94100faSBill Paul */
3245ff191365SJung-uk Kim re_set_rxmode(sc);
3246a94100faSBill Paul
3247483cc440SPyun YongHyeon /* Configure interrupt moderation. */
3248483cc440SPyun YongHyeon if (sc->rl_type == RL_8169) {
3249483cc440SPyun YongHyeon /* Magic from vendor. */
32505e6906eeSPyun YongHyeon CSR_WRITE_2(sc, RL_INTRMOD, 0x5100);
3251483cc440SPyun YongHyeon }
3252483cc440SPyun YongHyeon
32530f55f9d6SMarius Strobl /*
325414013280SMarius Strobl * Enable transmit and receive for RTL8168G and later controllers.
325514013280SMarius Strobl * RX/TX MACs should be enabled after RX/TX configuration.
32560f55f9d6SMarius Strobl */
325714013280SMarius Strobl if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0)
32580f55f9d6SMarius Strobl CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB | RL_CMD_RX_ENB);
32590f55f9d6SMarius Strobl
3260a94100faSBill Paul #ifdef DEVICE_POLLING
3261a94100faSBill Paul /*
3262a94100faSBill Paul * Disable interrupts if we are polling.
3263a94100faSBill Paul */
32644519a073SJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING)
3265a94100faSBill Paul CSR_WRITE_2(sc, RL_IMR, 0);
3266a94100faSBill Paul else /* otherwise ... */
326740929967SGleb Smirnoff #endif
3268ed510fb0SBill Paul
3269a94100faSBill Paul /*
3270a94100faSBill Paul * Enable interrupts.
3271a94100faSBill Paul */
3272a94100faSBill Paul if (sc->rl_testmode)
3273a94100faSBill Paul CSR_WRITE_2(sc, RL_IMR, 0);
3274a94100faSBill Paul else
3275a94100faSBill Paul CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS);
3276ed510fb0SBill Paul CSR_WRITE_2(sc, RL_ISR, RL_INTRS_CPLUS);
3277a94100faSBill Paul
3278a94100faSBill Paul /* Set initial TX threshold */
3279a94100faSBill Paul sc->rl_txthresh = RL_TX_THRESH_INIT;
3280a94100faSBill Paul
3281a94100faSBill Paul /* Start RX/TX process. */
3282a94100faSBill Paul CSR_WRITE_4(sc, RL_MISSEDPKT, 0);
3283a94100faSBill Paul
3284a94100faSBill Paul /*
3285a94100faSBill Paul * Initialize the timer interrupt register so that
3286a94100faSBill Paul * a timer interrupt will be generated once the timer
3287a94100faSBill Paul * reaches a certain number of ticks. The timer is
3288502be0f7SPyun YongHyeon * reloaded on each transmit.
3289502be0f7SPyun YongHyeon */
3290502be0f7SPyun YongHyeon #ifdef RE_TX_MODERATION
3291502be0f7SPyun YongHyeon /*
3292502be0f7SPyun YongHyeon * Use timer interrupt register to moderate TX interrupt
3293a94100faSBill Paul * moderation, which dramatically improves TX frame rate.
3294a94100faSBill Paul */
3295a94100faSBill Paul if (sc->rl_type == RL_8169)
3296a94100faSBill Paul CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800);
3297a94100faSBill Paul else
3298a94100faSBill Paul CSR_WRITE_4(sc, RL_TIMERINT, 0x400);
3299502be0f7SPyun YongHyeon #else
3300502be0f7SPyun YongHyeon /*
3301502be0f7SPyun YongHyeon * Use timer interrupt register to moderate RX interrupt
3302502be0f7SPyun YongHyeon * moderation.
3303502be0f7SPyun YongHyeon */
3304502be0f7SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 &&
3305502be0f7SPyun YongHyeon intr_filter == 0) {
3306502be0f7SPyun YongHyeon if (sc->rl_type == RL_8169)
3307502be0f7SPyun YongHyeon CSR_WRITE_4(sc, RL_TIMERINT_8169,
3308502be0f7SPyun YongHyeon RL_USECS(sc->rl_int_rx_mod));
3309502be0f7SPyun YongHyeon } else {
3310502be0f7SPyun YongHyeon if (sc->rl_type == RL_8169)
3311502be0f7SPyun YongHyeon CSR_WRITE_4(sc, RL_TIMERINT_8169, RL_USECS(0));
3312502be0f7SPyun YongHyeon }
3313ed510fb0SBill Paul #endif
3314a94100faSBill Paul
3315a94100faSBill Paul /*
3316a94100faSBill Paul * For 8169 gigE NICs, set the max allowed RX packet
3317a94100faSBill Paul * size so we can receive jumbo frames.
3318a94100faSBill Paul */
331989feeee4SPyun YongHyeon if (sc->rl_type == RL_8169) {
332081eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
332181eee0ebSPyun YongHyeon /*
332281eee0ebSPyun YongHyeon * For controllers that use new jumbo frame scheme,
33232df05392SSergey Kandaurov * set maximum size of jumbo frame depending on
332481eee0ebSPyun YongHyeon * controller revisions.
332581eee0ebSPyun YongHyeon */
33264519a073SJustin Hibbits if (if_getmtu(ifp) > RL_MTU)
332781eee0ebSPyun YongHyeon CSR_WRITE_2(sc, RL_MAXRXPKTLEN,
332881eee0ebSPyun YongHyeon sc->rl_hwrev->rl_max_mtu +
332981eee0ebSPyun YongHyeon ETHER_VLAN_ENCAP_LEN + ETHER_HDR_LEN +
333081eee0ebSPyun YongHyeon ETHER_CRC_LEN);
333189feeee4SPyun YongHyeon else
333281eee0ebSPyun YongHyeon CSR_WRITE_2(sc, RL_MAXRXPKTLEN,
333381eee0ebSPyun YongHyeon RE_RX_DESC_BUFLEN);
333481eee0ebSPyun YongHyeon } else if ((sc->rl_flags & RL_FLAG_PCIE) != 0 &&
333581eee0ebSPyun YongHyeon sc->rl_hwrev->rl_max_mtu == RL_MTU) {
333681eee0ebSPyun YongHyeon /* RTL810x has no jumbo frame support. */
333781eee0ebSPyun YongHyeon CSR_WRITE_2(sc, RL_MAXRXPKTLEN, RE_RX_DESC_BUFLEN);
333881eee0ebSPyun YongHyeon } else
3339a94100faSBill Paul CSR_WRITE_2(sc, RL_MAXRXPKTLEN, 16383);
334089feeee4SPyun YongHyeon }
3341a94100faSBill Paul
334297b9d4baSJohn-Mark Gurney if (sc->rl_testmode)
3343a94100faSBill Paul return;
3344a94100faSBill Paul
3345e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg1, CSR_READ_1(sc, sc->rl_cfg1) |
3346e7e7593cSPyun YongHyeon RL_CFG1_DRVLOAD);
3347a94100faSBill Paul
33484519a073SJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
33494519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
3350a94100faSBill Paul
3351351a76f9SPyun YongHyeon sc->rl_flags &= ~RL_FLAG_LINK;
33521662c49eSPyun YongHyeon mii_mediachg(mii);
33531662c49eSPyun YongHyeon
33541d545c7aSMarius Strobl sc->rl_watchdog_timer = 0;
3355d1754a9bSJohn Baldwin callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
335654bbcca4SVincenzo Maffione
335754bbcca4SVincenzo Maffione #ifdef DEV_NETMAP
335854bbcca4SVincenzo Maffione netmap_enable_all_rings(ifp);
335954bbcca4SVincenzo Maffione #endif /* DEV_NETMAP */
3360a94100faSBill Paul }
3361a94100faSBill Paul
3362a94100faSBill Paul /*
3363a94100faSBill Paul * Set media options.
3364a94100faSBill Paul */
3365a94100faSBill Paul static int
re_ifmedia_upd(if_t ifp)33664519a073SJustin Hibbits re_ifmedia_upd(if_t ifp)
3367a94100faSBill Paul {
3368a94100faSBill Paul struct rl_softc *sc;
3369a94100faSBill Paul struct mii_data *mii;
33706f0f9b12SPyun YongHyeon int error;
3371a94100faSBill Paul
33724519a073SJustin Hibbits sc = if_getsoftc(ifp);
3373a94100faSBill Paul mii = device_get_softc(sc->rl_miibus);
3374d1754a9bSJohn Baldwin RL_LOCK(sc);
33756f0f9b12SPyun YongHyeon error = mii_mediachg(mii);
3376d1754a9bSJohn Baldwin RL_UNLOCK(sc);
3377a94100faSBill Paul
33786f0f9b12SPyun YongHyeon return (error);
3379a94100faSBill Paul }
3380a94100faSBill Paul
3381a94100faSBill Paul /*
3382a94100faSBill Paul * Report current media status.
3383a94100faSBill Paul */
3384a94100faSBill Paul static void
re_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)33854519a073SJustin Hibbits re_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
3386a94100faSBill Paul {
3387a94100faSBill Paul struct rl_softc *sc;
3388a94100faSBill Paul struct mii_data *mii;
3389a94100faSBill Paul
33904519a073SJustin Hibbits sc = if_getsoftc(ifp);
3391a94100faSBill Paul mii = device_get_softc(sc->rl_miibus);
3392a94100faSBill Paul
3393d1754a9bSJohn Baldwin RL_LOCK(sc);
3394a94100faSBill Paul mii_pollstat(mii);
3395a94100faSBill Paul ifmr->ifm_active = mii->mii_media_active;
3396a94100faSBill Paul ifmr->ifm_status = mii->mii_media_status;
339757c81d92SPyun YongHyeon RL_UNLOCK(sc);
3398a94100faSBill Paul }
3399a94100faSBill Paul
3400a94100faSBill Paul static int
re_ioctl(if_t ifp,u_long command,caddr_t data)34014519a073SJustin Hibbits re_ioctl(if_t ifp, u_long command, caddr_t data)
3402a94100faSBill Paul {
34034519a073SJustin Hibbits struct rl_softc *sc = if_getsoftc(ifp);
3404a94100faSBill Paul struct ifreq *ifr = (struct ifreq *) data;
3405a94100faSBill Paul struct mii_data *mii;
340640929967SGleb Smirnoff int error = 0;
3407a94100faSBill Paul
3408a94100faSBill Paul switch (command) {
3409a94100faSBill Paul case SIOCSIFMTU:
341081eee0ebSPyun YongHyeon if (ifr->ifr_mtu < ETHERMIN ||
3411ab9f923eSPyun YongHyeon ifr->ifr_mtu > sc->rl_hwrev->rl_max_mtu ||
3412ab9f923eSPyun YongHyeon ((sc->rl_flags & RL_FLAG_FASTETHER) != 0 &&
3413ab9f923eSPyun YongHyeon ifr->ifr_mtu > RL_MTU)) {
3414c1d0b573SPyun YongHyeon error = EINVAL;
3415c1d0b573SPyun YongHyeon break;
3416c1d0b573SPyun YongHyeon }
3417c1d0b573SPyun YongHyeon RL_LOCK(sc);
34184519a073SJustin Hibbits if (if_getmtu(ifp) != ifr->ifr_mtu) {
34194519a073SJustin Hibbits if_setmtu(ifp, ifr->ifr_mtu);
342081eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
34214519a073SJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
34224519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
342381eee0ebSPyun YongHyeon re_init_locked(sc);
342481eee0ebSPyun YongHyeon }
34254519a073SJustin Hibbits if (if_getmtu(ifp) > RL_TSO_MTU &&
34264519a073SJustin Hibbits (if_getcapenable(ifp) & IFCAP_TSO4) != 0) {
34274519a073SJustin Hibbits if_setcapenablebit(ifp, 0,
34284519a073SJustin Hibbits IFCAP_TSO4 | IFCAP_VLAN_HWTSO);
34294519a073SJustin Hibbits if_sethwassistbits(ifp, 0, CSUM_TSO);
343081eee0ebSPyun YongHyeon }
3431ecafbbb5SPyun YongHyeon VLAN_CAPABILITIES(ifp);
3432ae644087SPyun YongHyeon }
3433d1754a9bSJohn Baldwin RL_UNLOCK(sc);
3434a94100faSBill Paul break;
3435a94100faSBill Paul case SIOCSIFFLAGS:
343697b9d4baSJohn-Mark Gurney RL_LOCK(sc);
34374519a073SJustin Hibbits if ((if_getflags(ifp) & IFF_UP) != 0) {
34384519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
34394519a073SJustin Hibbits if (((if_getflags(ifp) ^ sc->rl_if_flags)
34403021aef8SPyun YongHyeon & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
3441ff191365SJung-uk Kim re_set_rxmode(sc);
3442eed497bbSPyun YongHyeon } else
344397b9d4baSJohn-Mark Gurney re_init_locked(sc);
3444eed497bbSPyun YongHyeon } else {
34454519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
3446a94100faSBill Paul re_stop(sc);
3447eed497bbSPyun YongHyeon }
34484519a073SJustin Hibbits sc->rl_if_flags = if_getflags(ifp);
344997b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
3450a94100faSBill Paul break;
3451a94100faSBill Paul case SIOCADDMULTI:
3452a94100faSBill Paul case SIOCDELMULTI:
345397b9d4baSJohn-Mark Gurney RL_LOCK(sc);
34544519a073SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
3455ff191365SJung-uk Kim re_set_rxmode(sc);
345697b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
3457a94100faSBill Paul break;
3458a94100faSBill Paul case SIOCGIFMEDIA:
3459a94100faSBill Paul case SIOCSIFMEDIA:
3460a94100faSBill Paul mii = device_get_softc(sc->rl_miibus);
3461a94100faSBill Paul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
3462a94100faSBill Paul break;
3463a94100faSBill Paul case SIOCSIFCAP:
346440929967SGleb Smirnoff {
3465f051cb85SGleb Smirnoff int mask, reinit;
3466f051cb85SGleb Smirnoff
34674519a073SJustin Hibbits mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
3468f051cb85SGleb Smirnoff reinit = 0;
346940929967SGleb Smirnoff #ifdef DEVICE_POLLING
347040929967SGleb Smirnoff if (mask & IFCAP_POLLING) {
347140929967SGleb Smirnoff if (ifr->ifr_reqcap & IFCAP_POLLING) {
347240929967SGleb Smirnoff error = ether_poll_register(re_poll, ifp);
347340929967SGleb Smirnoff if (error)
347440929967SGleb Smirnoff return (error);
3475d1754a9bSJohn Baldwin RL_LOCK(sc);
347640929967SGleb Smirnoff /* Disable interrupts */
347740929967SGleb Smirnoff CSR_WRITE_2(sc, RL_IMR, 0x0000);
34784519a073SJustin Hibbits if_setcapenablebit(ifp, IFCAP_POLLING, 0);
347940929967SGleb Smirnoff RL_UNLOCK(sc);
348040929967SGleb Smirnoff } else {
348140929967SGleb Smirnoff error = ether_poll_deregister(ifp);
348240929967SGleb Smirnoff /* Enable interrupts. */
348340929967SGleb Smirnoff RL_LOCK(sc);
348440929967SGleb Smirnoff CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS);
34854519a073SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_POLLING);
348640929967SGleb Smirnoff RL_UNLOCK(sc);
348740929967SGleb Smirnoff }
348840929967SGleb Smirnoff }
348940929967SGleb Smirnoff #endif /* DEVICE_POLLING */
3490600af6c2SPyun YongHyeon RL_LOCK(sc);
3491d3b181aeSPyun YongHyeon if ((mask & IFCAP_TXCSUM) != 0 &&
34924519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_TXCSUM) != 0) {
34934519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_TXCSUM);
34944519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
34954519a073SJustin Hibbits if_sethwassistbits(ifp, RE_CSUM_FEATURES, 0);
349674a03446SPyun YongHyeon else
34974519a073SJustin Hibbits if_sethwassistbits(ifp, 0, RE_CSUM_FEATURES);
3498f051cb85SGleb Smirnoff reinit = 1;
349940929967SGleb Smirnoff }
3500d3b181aeSPyun YongHyeon if ((mask & IFCAP_RXCSUM) != 0 &&
35014519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
35024519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_RXCSUM);
3503d3b181aeSPyun YongHyeon reinit = 1;
3504d3b181aeSPyun YongHyeon }
3505ecafbbb5SPyun YongHyeon if ((mask & IFCAP_TSO4) != 0 &&
35064519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_TSO4) != 0) {
35074519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_TSO4);
35084519a073SJustin Hibbits if ((IFCAP_TSO4 & if_getcapenable(ifp)) != 0)
35094519a073SJustin Hibbits if_sethwassistbits(ifp, CSUM_TSO, 0);
3510dc74159dSPyun YongHyeon else
35114519a073SJustin Hibbits if_sethwassistbits(ifp, 0, CSUM_TSO);
35124519a073SJustin Hibbits if (if_getmtu(ifp) > RL_TSO_MTU &&
35134519a073SJustin Hibbits (if_getcapenable(ifp) & IFCAP_TSO4) != 0) {
35144519a073SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_TSO4);
35154519a073SJustin Hibbits if_sethwassistbits(ifp, 0, CSUM_TSO);
3516ae644087SPyun YongHyeon }
3517dc74159dSPyun YongHyeon }
3518ecafbbb5SPyun YongHyeon if ((mask & IFCAP_VLAN_HWTSO) != 0 &&
35194519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_VLAN_HWTSO) != 0)
35204519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
3521ecafbbb5SPyun YongHyeon if ((mask & IFCAP_VLAN_HWTAGGING) != 0 &&
35224519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_VLAN_HWTAGGING) != 0) {
35234519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
3524ecafbbb5SPyun YongHyeon /* TSO over VLAN requires VLAN hardware tagging. */
35254519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) == 0)
35264519a073SJustin Hibbits if_setcapenablebit(ifp, 0, IFCAP_VLAN_HWTSO);
3527ecafbbb5SPyun YongHyeon reinit = 1;
3528ecafbbb5SPyun YongHyeon }
352981eee0ebSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0 &&
353081eee0ebSPyun YongHyeon (mask & (IFCAP_HWCSUM | IFCAP_TSO4 |
353181eee0ebSPyun YongHyeon IFCAP_VLAN_HWTSO)) != 0)
353281eee0ebSPyun YongHyeon reinit = 1;
35337467bd53SPyun YongHyeon if ((mask & IFCAP_WOL) != 0 &&
35344519a073SJustin Hibbits (if_getcapabilities(ifp) & IFCAP_WOL) != 0) {
35357467bd53SPyun YongHyeon if ((mask & IFCAP_WOL_UCAST) != 0)
35364519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_WOL_UCAST);
35377467bd53SPyun YongHyeon if ((mask & IFCAP_WOL_MCAST) != 0)
35384519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_WOL_MCAST);
35397467bd53SPyun YongHyeon if ((mask & IFCAP_WOL_MAGIC) != 0)
35404519a073SJustin Hibbits if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
35417467bd53SPyun YongHyeon }
35424519a073SJustin Hibbits if (reinit && if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
35434519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
3544600af6c2SPyun YongHyeon re_init_locked(sc);
35458476c243SPyun YongHyeon }
3546600af6c2SPyun YongHyeon RL_UNLOCK(sc);
3547960fd5b3SPyun YongHyeon VLAN_CAPABILITIES(ifp);
354840929967SGleb Smirnoff }
3549a94100faSBill Paul break;
3550a94100faSBill Paul default:
3551a94100faSBill Paul error = ether_ioctl(ifp, command, data);
3552a94100faSBill Paul break;
3553a94100faSBill Paul }
3554a94100faSBill Paul
3555a94100faSBill Paul return (error);
3556a94100faSBill Paul }
3557a94100faSBill Paul
3558a94100faSBill Paul static void
re_watchdog(struct rl_softc * sc)35597b5ffebfSPyun YongHyeon re_watchdog(struct rl_softc *sc)
35601d545c7aSMarius Strobl {
35614519a073SJustin Hibbits if_t ifp;
3562a94100faSBill Paul
35631d545c7aSMarius Strobl RL_LOCK_ASSERT(sc);
35641d545c7aSMarius Strobl
35651d545c7aSMarius Strobl if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer != 0)
35661d545c7aSMarius Strobl return;
35671d545c7aSMarius Strobl
3568130b6dfbSPyun YongHyeon ifp = sc->rl_ifp;
3569a94100faSBill Paul re_txeof(sc);
3570130b6dfbSPyun YongHyeon if (sc->rl_ldata.rl_tx_free == sc->rl_ldata.rl_tx_desc_cnt) {
3571130b6dfbSPyun YongHyeon if_printf(ifp, "watchdog timeout (missed Tx interrupts) "
3572130b6dfbSPyun YongHyeon "-- recovering\n");
35734519a073SJustin Hibbits if (!if_sendq_empty(ifp))
3574d180a66fSPyun YongHyeon re_start_locked(ifp);
3575130b6dfbSPyun YongHyeon return;
3576130b6dfbSPyun YongHyeon }
3577130b6dfbSPyun YongHyeon
3578130b6dfbSPyun YongHyeon if_printf(ifp, "watchdog timeout\n");
3579c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
3580130b6dfbSPyun YongHyeon
35811abcdbd1SAttilio Rao re_rxeof(sc, NULL);
35824519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
358397b9d4baSJohn-Mark Gurney re_init_locked(sc);
35844519a073SJustin Hibbits if (!if_sendq_empty(ifp))
3585d180a66fSPyun YongHyeon re_start_locked(ifp);
3586a94100faSBill Paul }
3587a94100faSBill Paul
3588a94100faSBill Paul /*
3589a94100faSBill Paul * Stop the adapter and free any mbufs allocated to the
3590a94100faSBill Paul * RX and TX lists.
3591a94100faSBill Paul */
3592a94100faSBill Paul static void
re_stop(struct rl_softc * sc)35937b5ffebfSPyun YongHyeon re_stop(struct rl_softc *sc)
3594a94100faSBill Paul {
35950ce0868aSPyun YongHyeon int i;
35964519a073SJustin Hibbits if_t ifp;
3597d65abd66SPyun YongHyeon struct rl_txdesc *txd;
3598d65abd66SPyun YongHyeon struct rl_rxdesc *rxd;
3599a94100faSBill Paul
360097b9d4baSJohn-Mark Gurney RL_LOCK_ASSERT(sc);
360197b9d4baSJohn-Mark Gurney
3602fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
3603a94100faSBill Paul
36041d545c7aSMarius Strobl sc->rl_watchdog_timer = 0;
3605d1754a9bSJohn Baldwin callout_stop(&sc->rl_stat_callout);
36064519a073SJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
3607a94100faSBill Paul
360854bbcca4SVincenzo Maffione #ifdef DEV_NETMAP
360954bbcca4SVincenzo Maffione netmap_disable_all_rings(ifp);
361054bbcca4SVincenzo Maffione #endif /* DEV_NETMAP */
361154bbcca4SVincenzo Maffione
3612fcb220acSPyun YongHyeon /*
3613fcb220acSPyun YongHyeon * Disable accepting frames to put RX MAC into idle state.
3614fcb220acSPyun YongHyeon * Otherwise it's possible to get frames while stop command
3615fcb220acSPyun YongHyeon * execution is in progress and controller can DMA the frame
3616fcb220acSPyun YongHyeon * to already freed RX buffer during that period.
3617fcb220acSPyun YongHyeon */
3618fcb220acSPyun YongHyeon CSR_WRITE_4(sc, RL_RXCFG, CSR_READ_4(sc, RL_RXCFG) &
3619fcb220acSPyun YongHyeon ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_INDIV | RL_RXCFG_RX_MULTI |
3620fcb220acSPyun YongHyeon RL_RXCFG_RX_BROAD));
3621fcb220acSPyun YongHyeon
362214013280SMarius Strobl if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) {
362314013280SMarius Strobl /* Enable RXDV gate. */
362414013280SMarius Strobl CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) |
362514013280SMarius Strobl 0x00080000);
362614013280SMarius Strobl }
362714013280SMarius Strobl
3628eef0e496SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_WAIT_TXPOLL) != 0) {
3629eef0e496SPyun YongHyeon for (i = RL_TIMEOUT; i > 0; i--) {
3630eef0e496SPyun YongHyeon if ((CSR_READ_1(sc, sc->rl_txstart) &
3631eef0e496SPyun YongHyeon RL_TXSTART_START) == 0)
3632eef0e496SPyun YongHyeon break;
3633eef0e496SPyun YongHyeon DELAY(20);
3634eef0e496SPyun YongHyeon }
3635eef0e496SPyun YongHyeon if (i == 0)
3636eef0e496SPyun YongHyeon device_printf(sc->rl_dev,
3637eef0e496SPyun YongHyeon "stopping TX poll timed out!\n");
3638eef0e496SPyun YongHyeon CSR_WRITE_1(sc, RL_COMMAND, 0x00);
3639eef0e496SPyun YongHyeon } else if ((sc->rl_flags & RL_FLAG_CMDSTOP) != 0) {
3640ead8fc66SPyun YongHyeon CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_STOPREQ | RL_CMD_TX_ENB |
3641ead8fc66SPyun YongHyeon RL_CMD_RX_ENB);
3642eef0e496SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_CMDSTOP_WAIT_TXQ) != 0) {
3643eef0e496SPyun YongHyeon for (i = RL_TIMEOUT; i > 0; i--) {
3644eef0e496SPyun YongHyeon if ((CSR_READ_4(sc, RL_TXCFG) &
3645eef0e496SPyun YongHyeon RL_TXCFG_QUEUE_EMPTY) != 0)
3646eef0e496SPyun YongHyeon break;
3647eef0e496SPyun YongHyeon DELAY(100);
3648eef0e496SPyun YongHyeon }
3649eef0e496SPyun YongHyeon if (i == 0)
3650eef0e496SPyun YongHyeon device_printf(sc->rl_dev,
3651eef0e496SPyun YongHyeon "stopping TXQ timed out!\n");
3652eef0e496SPyun YongHyeon }
3653eef0e496SPyun YongHyeon } else
3654a94100faSBill Paul CSR_WRITE_1(sc, RL_COMMAND, 0x00);
3655ead8fc66SPyun YongHyeon DELAY(1000);
3656a94100faSBill Paul CSR_WRITE_2(sc, RL_IMR, 0x0000);
3657ed510fb0SBill Paul CSR_WRITE_2(sc, RL_ISR, 0xFFFF);
3658a94100faSBill Paul
3659a94100faSBill Paul if (sc->rl_head != NULL) {
3660a94100faSBill Paul m_freem(sc->rl_head);
3661a94100faSBill Paul sc->rl_head = sc->rl_tail = NULL;
3662a94100faSBill Paul }
3663a94100faSBill Paul
3664a94100faSBill Paul /* Free the TX list buffers. */
3665d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) {
3666d65abd66SPyun YongHyeon txd = &sc->rl_ldata.rl_tx_desc[i];
3667d65abd66SPyun YongHyeon if (txd->tx_m != NULL) {
3668d65abd66SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag,
3669d65abd66SPyun YongHyeon txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
3670d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag,
3671d65abd66SPyun YongHyeon txd->tx_dmamap);
3672d65abd66SPyun YongHyeon m_freem(txd->tx_m);
3673d65abd66SPyun YongHyeon txd->tx_m = NULL;
3674a94100faSBill Paul }
3675a94100faSBill Paul }
3676a94100faSBill Paul
3677a94100faSBill Paul /* Free the RX list buffers. */
3678d65abd66SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
3679d65abd66SPyun YongHyeon rxd = &sc->rl_ldata.rl_rx_desc[i];
3680d65abd66SPyun YongHyeon if (rxd->rx_m != NULL) {
3681cba16362SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag,
3682d65abd66SPyun YongHyeon rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
3683d65abd66SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag,
3684d65abd66SPyun YongHyeon rxd->rx_dmamap);
3685d65abd66SPyun YongHyeon m_freem(rxd->rx_m);
3686d65abd66SPyun YongHyeon rxd->rx_m = NULL;
3687a94100faSBill Paul }
3688a94100faSBill Paul }
36891f32d3b7SPyun YongHyeon
36901f32d3b7SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
36911f32d3b7SPyun YongHyeon for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) {
36921f32d3b7SPyun YongHyeon rxd = &sc->rl_ldata.rl_jrx_desc[i];
36931f32d3b7SPyun YongHyeon if (rxd->rx_m != NULL) {
36941f32d3b7SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_jrx_mtag,
36951f32d3b7SPyun YongHyeon rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
36961f32d3b7SPyun YongHyeon bus_dmamap_unload(sc->rl_ldata.rl_jrx_mtag,
36971f32d3b7SPyun YongHyeon rxd->rx_dmamap);
36981f32d3b7SPyun YongHyeon m_freem(rxd->rx_m);
36991f32d3b7SPyun YongHyeon rxd->rx_m = NULL;
37001f32d3b7SPyun YongHyeon }
37011f32d3b7SPyun YongHyeon }
37021f32d3b7SPyun YongHyeon }
3703a94100faSBill Paul }
3704a94100faSBill Paul
3705a94100faSBill Paul /*
3706a94100faSBill Paul * Device suspend routine. Stop the interface and save some PCI
3707a94100faSBill Paul * settings in case the BIOS doesn't restore them properly on
3708a94100faSBill Paul * resume.
3709a94100faSBill Paul */
3710a94100faSBill Paul static int
re_suspend(device_t dev)37117b5ffebfSPyun YongHyeon re_suspend(device_t dev)
3712a94100faSBill Paul {
3713a94100faSBill Paul struct rl_softc *sc;
3714a94100faSBill Paul
3715a94100faSBill Paul sc = device_get_softc(dev);
3716a94100faSBill Paul
371797b9d4baSJohn-Mark Gurney RL_LOCK(sc);
3718a94100faSBill Paul re_stop(sc);
37197467bd53SPyun YongHyeon re_setwol(sc);
3720a94100faSBill Paul sc->suspended = 1;
372197b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
3722a94100faSBill Paul
3723a94100faSBill Paul return (0);
3724a94100faSBill Paul }
3725a94100faSBill Paul
3726a94100faSBill Paul /*
3727a94100faSBill Paul * Device resume routine. Restore some PCI settings in case the BIOS
3728a94100faSBill Paul * doesn't, re-enable busmastering, and restart the interface if
3729a94100faSBill Paul * appropriate.
3730a94100faSBill Paul */
3731a94100faSBill Paul static int
re_resume(device_t dev)37327b5ffebfSPyun YongHyeon re_resume(device_t dev)
3733a94100faSBill Paul {
3734a94100faSBill Paul struct rl_softc *sc;
37354519a073SJustin Hibbits if_t ifp;
3736a94100faSBill Paul
3737a94100faSBill Paul sc = device_get_softc(dev);
373897b9d4baSJohn-Mark Gurney
373997b9d4baSJohn-Mark Gurney RL_LOCK(sc);
374097b9d4baSJohn-Mark Gurney
3741fc74a9f9SBrooks Davis ifp = sc->rl_ifp;
374261f45a72SPyun YongHyeon /* Take controller out of sleep mode. */
374361f45a72SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
374461f45a72SPyun YongHyeon if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
374561f45a72SPyun YongHyeon CSR_WRITE_1(sc, RL_GPIO,
374661f45a72SPyun YongHyeon CSR_READ_1(sc, RL_GPIO) | 0x01);
374761f45a72SPyun YongHyeon }
3748a94100faSBill Paul
37497467bd53SPyun YongHyeon /*
37507467bd53SPyun YongHyeon * Clear WOL matching such that normal Rx filtering
37517467bd53SPyun YongHyeon * wouldn't interfere with WOL patterns.
37527467bd53SPyun YongHyeon */
37537467bd53SPyun YongHyeon re_clrwol(sc);
375401d1a6c3SPyun YongHyeon
375501d1a6c3SPyun YongHyeon /* reinitialize interface if necessary */
37564519a073SJustin Hibbits if (if_getflags(ifp) & IFF_UP)
375701d1a6c3SPyun YongHyeon re_init_locked(sc);
375801d1a6c3SPyun YongHyeon
3759a94100faSBill Paul sc->suspended = 0;
376097b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
3761a94100faSBill Paul
3762a94100faSBill Paul return (0);
3763a94100faSBill Paul }
3764a94100faSBill Paul
3765a94100faSBill Paul /*
3766a94100faSBill Paul * Stop all chip I/O so that the kernel's probe routines don't
3767a94100faSBill Paul * get confused by errant DMAs when rebooting.
3768a94100faSBill Paul */
37696a087a87SPyun YongHyeon static int
re_shutdown(device_t dev)37707b5ffebfSPyun YongHyeon re_shutdown(device_t dev)
3771a94100faSBill Paul {
3772a94100faSBill Paul struct rl_softc *sc;
3773a94100faSBill Paul
3774a94100faSBill Paul sc = device_get_softc(dev);
3775a94100faSBill Paul
377697b9d4baSJohn-Mark Gurney RL_LOCK(sc);
3777a94100faSBill Paul re_stop(sc);
3778536fde34SMaxim Sobolev /*
3779536fde34SMaxim Sobolev * Mark interface as down since otherwise we will panic if
3780536fde34SMaxim Sobolev * interrupt comes in later on, which can happen in some
378172293673SRuslan Ermilov * cases.
3782536fde34SMaxim Sobolev */
37834519a073SJustin Hibbits if_setflagbits(sc->rl_ifp, 0, IFF_UP);
37847467bd53SPyun YongHyeon re_setwol(sc);
378597b9d4baSJohn-Mark Gurney RL_UNLOCK(sc);
37866a087a87SPyun YongHyeon
37876a087a87SPyun YongHyeon return (0);
3788a94100faSBill Paul }
37897467bd53SPyun YongHyeon
37907467bd53SPyun YongHyeon static void
re_set_linkspeed(struct rl_softc * sc)37916830588dSPyun YongHyeon re_set_linkspeed(struct rl_softc *sc)
37926830588dSPyun YongHyeon {
37936830588dSPyun YongHyeon struct mii_softc *miisc;
37946830588dSPyun YongHyeon struct mii_data *mii;
37956830588dSPyun YongHyeon int aneg, i, phyno;
37966830588dSPyun YongHyeon
37976830588dSPyun YongHyeon RL_LOCK_ASSERT(sc);
37986830588dSPyun YongHyeon
37996830588dSPyun YongHyeon mii = device_get_softc(sc->rl_miibus);
38006830588dSPyun YongHyeon mii_pollstat(mii);
38016830588dSPyun YongHyeon aneg = 0;
38026830588dSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
38036830588dSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) {
38046830588dSPyun YongHyeon switch IFM_SUBTYPE(mii->mii_media_active) {
38056830588dSPyun YongHyeon case IFM_10_T:
38066830588dSPyun YongHyeon case IFM_100_TX:
38076830588dSPyun YongHyeon return;
38086830588dSPyun YongHyeon case IFM_1000_T:
38096830588dSPyun YongHyeon aneg++;
38106830588dSPyun YongHyeon break;
38116830588dSPyun YongHyeon default:
38126830588dSPyun YongHyeon break;
38136830588dSPyun YongHyeon }
38146830588dSPyun YongHyeon }
38156830588dSPyun YongHyeon miisc = LIST_FIRST(&mii->mii_phys);
38166830588dSPyun YongHyeon phyno = miisc->mii_phy;
38176830588dSPyun YongHyeon LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
38186830588dSPyun YongHyeon PHY_RESET(miisc);
38196830588dSPyun YongHyeon re_miibus_writereg(sc->rl_dev, phyno, MII_100T2CR, 0);
38206830588dSPyun YongHyeon re_miibus_writereg(sc->rl_dev, phyno,
38216830588dSPyun YongHyeon MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA);
38226830588dSPyun YongHyeon re_miibus_writereg(sc->rl_dev, phyno,
38236830588dSPyun YongHyeon MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
38246830588dSPyun YongHyeon DELAY(1000);
38256830588dSPyun YongHyeon if (aneg != 0) {
38266830588dSPyun YongHyeon /*
38276830588dSPyun YongHyeon * Poll link state until re(4) get a 10/100Mbps link.
38286830588dSPyun YongHyeon */
38296830588dSPyun YongHyeon for (i = 0; i < MII_ANEGTICKS_GIGE; i++) {
38306830588dSPyun YongHyeon mii_pollstat(mii);
38316830588dSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID))
38326830588dSPyun YongHyeon == (IFM_ACTIVE | IFM_AVALID)) {
38336830588dSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) {
38346830588dSPyun YongHyeon case IFM_10_T:
38356830588dSPyun YongHyeon case IFM_100_TX:
38366830588dSPyun YongHyeon return;
38376830588dSPyun YongHyeon default:
38386830588dSPyun YongHyeon break;
38396830588dSPyun YongHyeon }
38406830588dSPyun YongHyeon }
38416830588dSPyun YongHyeon RL_UNLOCK(sc);
38426830588dSPyun YongHyeon pause("relnk", hz);
38436830588dSPyun YongHyeon RL_LOCK(sc);
38446830588dSPyun YongHyeon }
38456830588dSPyun YongHyeon if (i == MII_ANEGTICKS_GIGE)
38466830588dSPyun YongHyeon device_printf(sc->rl_dev,
38476830588dSPyun YongHyeon "establishing a link failed, WOL may not work!");
38486830588dSPyun YongHyeon }
38496830588dSPyun YongHyeon /*
38506830588dSPyun YongHyeon * No link, force MAC to have 100Mbps, full-duplex link.
38516830588dSPyun YongHyeon * MAC does not require reprogramming on resolved speed/duplex,
38526830588dSPyun YongHyeon * so this is just for completeness.
38536830588dSPyun YongHyeon */
38546830588dSPyun YongHyeon mii->mii_media_status = IFM_AVALID | IFM_ACTIVE;
38556830588dSPyun YongHyeon mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
38566830588dSPyun YongHyeon }
38576830588dSPyun YongHyeon
38586830588dSPyun YongHyeon static void
re_setwol(struct rl_softc * sc)38597b5ffebfSPyun YongHyeon re_setwol(struct rl_softc *sc)
38607467bd53SPyun YongHyeon {
38614519a073SJustin Hibbits if_t ifp;
38627467bd53SPyun YongHyeon uint8_t v;
38637467bd53SPyun YongHyeon
38647467bd53SPyun YongHyeon RL_LOCK_ASSERT(sc);
38657467bd53SPyun YongHyeon
3866*ddaf6524SJohn Baldwin if (!pci_has_pm(sc->rl_dev))
38677467bd53SPyun YongHyeon return;
38687467bd53SPyun YongHyeon
38697467bd53SPyun YongHyeon ifp = sc->rl_ifp;
387061f45a72SPyun YongHyeon /* Put controller into sleep mode. */
387161f45a72SPyun YongHyeon if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) {
387261f45a72SPyun YongHyeon if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80)
387361f45a72SPyun YongHyeon CSR_WRITE_1(sc, RL_GPIO,
387461f45a72SPyun YongHyeon CSR_READ_1(sc, RL_GPIO) & ~0x01);
387561f45a72SPyun YongHyeon }
38764519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL) != 0) {
3877e9f8886eSMarius Strobl if ((sc->rl_flags & RL_FLAG_8168G_PLUS) != 0) {
3878e9f8886eSMarius Strobl /* Disable RXDV gate. */
3879e9f8886eSMarius Strobl CSR_WRITE_4(sc, RL_MISC, CSR_READ_4(sc, RL_MISC) &
3880e9f8886eSMarius Strobl ~0x00080000);
3881e9f8886eSMarius Strobl }
3882fcb220acSPyun YongHyeon re_set_rxmode(sc);
38836830588dSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_WOL_MANLINK) != 0)
38846830588dSPyun YongHyeon re_set_linkspeed(sc);
3885fcb220acSPyun YongHyeon if ((sc->rl_flags & RL_FLAG_WOLRXENB) != 0)
3886886ff602SPyun YongHyeon CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RX_ENB);
3887fcb220acSPyun YongHyeon }
38887467bd53SPyun YongHyeon /* Enable config register write. */
38897467bd53SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
38907467bd53SPyun YongHyeon
38917467bd53SPyun YongHyeon /* Enable PME. */
3892e7e7593cSPyun YongHyeon v = CSR_READ_1(sc, sc->rl_cfg1);
38937467bd53SPyun YongHyeon v &= ~RL_CFG1_PME;
38944519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL) != 0)
38957467bd53SPyun YongHyeon v |= RL_CFG1_PME;
3896e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg1, v);
38977467bd53SPyun YongHyeon
3898e7e7593cSPyun YongHyeon v = CSR_READ_1(sc, sc->rl_cfg3);
38997467bd53SPyun YongHyeon v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
39004519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
39017467bd53SPyun YongHyeon v |= RL_CFG3_WOL_MAGIC;
3902e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg3, v);
39037467bd53SPyun YongHyeon
3904e7e7593cSPyun YongHyeon v = CSR_READ_1(sc, sc->rl_cfg5);
390544f7cbf5SPyun YongHyeon v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST |
390644f7cbf5SPyun YongHyeon RL_CFG5_WOL_LANWAKE);
39074519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL_UCAST) != 0)
39087467bd53SPyun YongHyeon v |= RL_CFG5_WOL_UCAST;
39094519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL_MCAST) != 0)
39107467bd53SPyun YongHyeon v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST;
39114519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL) != 0)
39127467bd53SPyun YongHyeon v |= RL_CFG5_WOL_LANWAKE;
3913e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg5, v);
39147467bd53SPyun YongHyeon
391544f7cbf5SPyun YongHyeon /* Config register write done. */
391644f7cbf5SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
391744f7cbf5SPyun YongHyeon
39184519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL) == 0 &&
3919d0c45156SPyun YongHyeon (sc->rl_flags & RL_FLAG_PHYWAKE_PM) != 0)
3920d0c45156SPyun YongHyeon CSR_WRITE_1(sc, RL_PMCH, CSR_READ_1(sc, RL_PMCH) & ~0x80);
39217467bd53SPyun YongHyeon /*
39227467bd53SPyun YongHyeon * It seems that hardware resets its link speed to 100Mbps in
39237467bd53SPyun YongHyeon * power down mode so switching to 100Mbps in driver is not
39247467bd53SPyun YongHyeon * needed.
39257467bd53SPyun YongHyeon */
39267467bd53SPyun YongHyeon
39277467bd53SPyun YongHyeon /* Request PME if WOL is requested. */
39284519a073SJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL) != 0)
3929*ddaf6524SJohn Baldwin pci_enable_pme(sc->rl_dev);
39307467bd53SPyun YongHyeon }
39317467bd53SPyun YongHyeon
39327467bd53SPyun YongHyeon static void
re_clrwol(struct rl_softc * sc)39337b5ffebfSPyun YongHyeon re_clrwol(struct rl_softc *sc)
39347467bd53SPyun YongHyeon {
39357467bd53SPyun YongHyeon uint8_t v;
39367467bd53SPyun YongHyeon
39377467bd53SPyun YongHyeon RL_LOCK_ASSERT(sc);
39387467bd53SPyun YongHyeon
3939*ddaf6524SJohn Baldwin if (!pci_has_pm(sc->rl_dev))
39407467bd53SPyun YongHyeon return;
39417467bd53SPyun YongHyeon
39427467bd53SPyun YongHyeon /* Enable config register write. */
39437467bd53SPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
39447467bd53SPyun YongHyeon
3945e7e7593cSPyun YongHyeon v = CSR_READ_1(sc, sc->rl_cfg3);
39467467bd53SPyun YongHyeon v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
3947e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg3, v);
39487467bd53SPyun YongHyeon
39497467bd53SPyun YongHyeon /* Config register write done. */
3950f98dd8cfSPyun YongHyeon CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
39517467bd53SPyun YongHyeon
3952e7e7593cSPyun YongHyeon v = CSR_READ_1(sc, sc->rl_cfg5);
39537467bd53SPyun YongHyeon v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
39547467bd53SPyun YongHyeon v &= ~RL_CFG5_WOL_LANWAKE;
3955e7e7593cSPyun YongHyeon CSR_WRITE_1(sc, sc->rl_cfg5, v);
39567467bd53SPyun YongHyeon }
39570534aae0SPyun YongHyeon
39580534aae0SPyun YongHyeon static void
re_add_sysctls(struct rl_softc * sc)39590534aae0SPyun YongHyeon re_add_sysctls(struct rl_softc *sc)
39600534aae0SPyun YongHyeon {
39610534aae0SPyun YongHyeon struct sysctl_ctx_list *ctx;
39620534aae0SPyun YongHyeon struct sysctl_oid_list *children;
3963502be0f7SPyun YongHyeon int error;
39640534aae0SPyun YongHyeon
39650534aae0SPyun YongHyeon ctx = device_get_sysctl_ctx(sc->rl_dev);
39660534aae0SPyun YongHyeon children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev));
39670534aae0SPyun YongHyeon
39680534aae0SPyun YongHyeon SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "stats",
39697029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
39707029da5cSPawel Biernacki re_sysctl_stats, "I", "Statistics Information");
3971502be0f7SPyun YongHyeon if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0)
3972502be0f7SPyun YongHyeon return;
3973502be0f7SPyun YongHyeon
3974502be0f7SPyun YongHyeon SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "int_rx_mod",
39757029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
39767029da5cSPawel Biernacki &sc->rl_int_rx_mod, 0, sysctl_hw_re_int_mod, "I",
39777029da5cSPawel Biernacki "re RX interrupt moderation");
3978502be0f7SPyun YongHyeon /* Pull in device tunables. */
3979502be0f7SPyun YongHyeon sc->rl_int_rx_mod = RL_TIMER_DEFAULT;
3980502be0f7SPyun YongHyeon error = resource_int_value(device_get_name(sc->rl_dev),
3981502be0f7SPyun YongHyeon device_get_unit(sc->rl_dev), "int_rx_mod", &sc->rl_int_rx_mod);
3982502be0f7SPyun YongHyeon if (error == 0) {
3983502be0f7SPyun YongHyeon if (sc->rl_int_rx_mod < RL_TIMER_MIN ||
3984502be0f7SPyun YongHyeon sc->rl_int_rx_mod > RL_TIMER_MAX) {
3985502be0f7SPyun YongHyeon device_printf(sc->rl_dev, "int_rx_mod value out of "
3986502be0f7SPyun YongHyeon "range; using default: %d\n",
3987502be0f7SPyun YongHyeon RL_TIMER_DEFAULT);
3988502be0f7SPyun YongHyeon sc->rl_int_rx_mod = RL_TIMER_DEFAULT;
3989502be0f7SPyun YongHyeon }
3990502be0f7SPyun YongHyeon }
39910534aae0SPyun YongHyeon }
39920534aae0SPyun YongHyeon
39930534aae0SPyun YongHyeon static int
re_sysctl_stats(SYSCTL_HANDLER_ARGS)39940534aae0SPyun YongHyeon re_sysctl_stats(SYSCTL_HANDLER_ARGS)
39950534aae0SPyun YongHyeon {
39960534aae0SPyun YongHyeon struct rl_softc *sc;
39970534aae0SPyun YongHyeon struct rl_stats *stats;
39980534aae0SPyun YongHyeon int error, i, result;
39990534aae0SPyun YongHyeon
40000534aae0SPyun YongHyeon result = -1;
40010534aae0SPyun YongHyeon error = sysctl_handle_int(oidp, &result, 0, req);
40020534aae0SPyun YongHyeon if (error || req->newptr == NULL)
40030534aae0SPyun YongHyeon return (error);
40040534aae0SPyun YongHyeon
40050534aae0SPyun YongHyeon if (result == 1) {
40060534aae0SPyun YongHyeon sc = (struct rl_softc *)arg1;
40070534aae0SPyun YongHyeon RL_LOCK(sc);
40084519a073SJustin Hibbits if ((if_getdrvflags(sc->rl_ifp) & IFF_DRV_RUNNING) == 0) {
400916a4824bSPyun YongHyeon RL_UNLOCK(sc);
401016a4824bSPyun YongHyeon goto done;
401116a4824bSPyun YongHyeon }
40120534aae0SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_stag,
40130534aae0SPyun YongHyeon sc->rl_ldata.rl_smap, BUS_DMASYNC_PREREAD);
40140534aae0SPyun YongHyeon CSR_WRITE_4(sc, RL_DUMPSTATS_HI,
40150534aae0SPyun YongHyeon RL_ADDR_HI(sc->rl_ldata.rl_stats_addr));
40160534aae0SPyun YongHyeon CSR_WRITE_4(sc, RL_DUMPSTATS_LO,
40170534aae0SPyun YongHyeon RL_ADDR_LO(sc->rl_ldata.rl_stats_addr));
40180534aae0SPyun YongHyeon CSR_WRITE_4(sc, RL_DUMPSTATS_LO,
40190534aae0SPyun YongHyeon RL_ADDR_LO(sc->rl_ldata.rl_stats_addr |
40200534aae0SPyun YongHyeon RL_DUMPSTATS_START));
40210534aae0SPyun YongHyeon for (i = RL_TIMEOUT; i > 0; i--) {
40220534aae0SPyun YongHyeon if ((CSR_READ_4(sc, RL_DUMPSTATS_LO) &
40230534aae0SPyun YongHyeon RL_DUMPSTATS_START) == 0)
40240534aae0SPyun YongHyeon break;
40250534aae0SPyun YongHyeon DELAY(1000);
40260534aae0SPyun YongHyeon }
40270534aae0SPyun YongHyeon bus_dmamap_sync(sc->rl_ldata.rl_stag,
40280534aae0SPyun YongHyeon sc->rl_ldata.rl_smap, BUS_DMASYNC_POSTREAD);
40290534aae0SPyun YongHyeon RL_UNLOCK(sc);
40300534aae0SPyun YongHyeon if (i == 0) {
40310534aae0SPyun YongHyeon device_printf(sc->rl_dev,
40320534aae0SPyun YongHyeon "DUMP statistics request timed out\n");
40330534aae0SPyun YongHyeon return (ETIMEDOUT);
40340534aae0SPyun YongHyeon }
403516a4824bSPyun YongHyeon done:
40360534aae0SPyun YongHyeon stats = sc->rl_ldata.rl_stats;
40370534aae0SPyun YongHyeon printf("%s statistics:\n", device_get_nameunit(sc->rl_dev));
40380534aae0SPyun YongHyeon printf("Tx frames : %ju\n",
40390534aae0SPyun YongHyeon (uintmax_t)le64toh(stats->rl_tx_pkts));
40400534aae0SPyun YongHyeon printf("Rx frames : %ju\n",
40410534aae0SPyun YongHyeon (uintmax_t)le64toh(stats->rl_rx_pkts));
40420534aae0SPyun YongHyeon printf("Tx errors : %ju\n",
40430534aae0SPyun YongHyeon (uintmax_t)le64toh(stats->rl_tx_errs));
40440534aae0SPyun YongHyeon printf("Rx errors : %u\n",
40450534aae0SPyun YongHyeon le32toh(stats->rl_rx_errs));
40460534aae0SPyun YongHyeon printf("Rx missed frames : %u\n",
40470534aae0SPyun YongHyeon (uint32_t)le16toh(stats->rl_missed_pkts));
40480534aae0SPyun YongHyeon printf("Rx frame alignment errs : %u\n",
40490534aae0SPyun YongHyeon (uint32_t)le16toh(stats->rl_rx_framealign_errs));
40500534aae0SPyun YongHyeon printf("Tx single collisions : %u\n",
40510534aae0SPyun YongHyeon le32toh(stats->rl_tx_onecoll));
40520534aae0SPyun YongHyeon printf("Tx multiple collisions : %u\n",
40530534aae0SPyun YongHyeon le32toh(stats->rl_tx_multicolls));
40540534aae0SPyun YongHyeon printf("Rx unicast frames : %ju\n",
40550534aae0SPyun YongHyeon (uintmax_t)le64toh(stats->rl_rx_ucasts));
40560534aae0SPyun YongHyeon printf("Rx broadcast frames : %ju\n",
40570534aae0SPyun YongHyeon (uintmax_t)le64toh(stats->rl_rx_bcasts));
40580534aae0SPyun YongHyeon printf("Rx multicast frames : %u\n",
40590534aae0SPyun YongHyeon le32toh(stats->rl_rx_mcasts));
40600534aae0SPyun YongHyeon printf("Tx aborts : %u\n",
40610534aae0SPyun YongHyeon (uint32_t)le16toh(stats->rl_tx_aborts));
40620534aae0SPyun YongHyeon printf("Tx underruns : %u\n",
40630534aae0SPyun YongHyeon (uint32_t)le16toh(stats->rl_rx_underruns));
40640534aae0SPyun YongHyeon }
40650534aae0SPyun YongHyeon
40660534aae0SPyun YongHyeon return (error);
40670534aae0SPyun YongHyeon }
4068502be0f7SPyun YongHyeon
4069502be0f7SPyun YongHyeon static int
sysctl_int_range(SYSCTL_HANDLER_ARGS,int low,int high)4070502be0f7SPyun YongHyeon sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
4071502be0f7SPyun YongHyeon {
4072502be0f7SPyun YongHyeon int error, value;
4073502be0f7SPyun YongHyeon
4074502be0f7SPyun YongHyeon if (arg1 == NULL)
4075502be0f7SPyun YongHyeon return (EINVAL);
4076502be0f7SPyun YongHyeon value = *(int *)arg1;
4077502be0f7SPyun YongHyeon error = sysctl_handle_int(oidp, &value, 0, req);
4078502be0f7SPyun YongHyeon if (error || req->newptr == NULL)
4079502be0f7SPyun YongHyeon return (error);
4080502be0f7SPyun YongHyeon if (value < low || value > high)
4081502be0f7SPyun YongHyeon return (EINVAL);
4082502be0f7SPyun YongHyeon *(int *)arg1 = value;
4083502be0f7SPyun YongHyeon
4084502be0f7SPyun YongHyeon return (0);
4085502be0f7SPyun YongHyeon }
4086502be0f7SPyun YongHyeon
4087502be0f7SPyun YongHyeon static int
sysctl_hw_re_int_mod(SYSCTL_HANDLER_ARGS)4088502be0f7SPyun YongHyeon sysctl_hw_re_int_mod(SYSCTL_HANDLER_ARGS)
4089502be0f7SPyun YongHyeon {
4090502be0f7SPyun YongHyeon
4091502be0f7SPyun YongHyeon return (sysctl_int_range(oidp, arg1, arg2, req, RL_TIMER_MIN,
4092502be0f7SPyun YongHyeon RL_TIMER_MAX));
4093502be0f7SPyun YongHyeon }
4094306c97e2SMark Johnston
40957790c8c1SConrad Meyer #ifdef DEBUGNET
4096306c97e2SMark Johnston static void
re_debugnet_init(if_t ifp,int * nrxr,int * ncl,int * clsize)40974519a073SJustin Hibbits re_debugnet_init(if_t ifp, int *nrxr, int *ncl, int *clsize)
4098306c97e2SMark Johnston {
4099306c97e2SMark Johnston struct rl_softc *sc;
4100306c97e2SMark Johnston
4101306c97e2SMark Johnston sc = if_getsoftc(ifp);
4102306c97e2SMark Johnston RL_LOCK(sc);
4103306c97e2SMark Johnston *nrxr = sc->rl_ldata.rl_rx_desc_cnt;
41047790c8c1SConrad Meyer *ncl = DEBUGNET_MAX_IN_FLIGHT;
41054519a073SJustin Hibbits *clsize = (if_getmtu(ifp) > RL_MTU &&
4106306c97e2SMark Johnston (sc->rl_flags & RL_FLAG_JUMBOV2) != 0) ? MJUM9BYTES : MCLBYTES;
4107306c97e2SMark Johnston RL_UNLOCK(sc);
4108306c97e2SMark Johnston }
4109306c97e2SMark Johnston
4110306c97e2SMark Johnston static void
re_debugnet_event(if_t ifp __unused,enum debugnet_ev event __unused)41114519a073SJustin Hibbits re_debugnet_event(if_t ifp __unused, enum debugnet_ev event __unused)
4112306c97e2SMark Johnston {
4113306c97e2SMark Johnston }
4114306c97e2SMark Johnston
4115306c97e2SMark Johnston static int
re_debugnet_transmit(if_t ifp,struct mbuf * m)41164519a073SJustin Hibbits re_debugnet_transmit(if_t ifp, struct mbuf *m)
4117306c97e2SMark Johnston {
4118306c97e2SMark Johnston struct rl_softc *sc;
4119306c97e2SMark Johnston int error;
4120306c97e2SMark Johnston
4121306c97e2SMark Johnston sc = if_getsoftc(ifp);
4122306c97e2SMark Johnston if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
4123306c97e2SMark Johnston IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0)
4124306c97e2SMark Johnston return (EBUSY);
4125306c97e2SMark Johnston
4126306c97e2SMark Johnston error = re_encap(sc, &m);
4127306c97e2SMark Johnston if (error == 0)
4128306c97e2SMark Johnston re_start_tx(sc);
4129306c97e2SMark Johnston return (error);
4130306c97e2SMark Johnston }
4131306c97e2SMark Johnston
4132306c97e2SMark Johnston static int
re_debugnet_poll(if_t ifp,int count)41334519a073SJustin Hibbits re_debugnet_poll(if_t ifp, int count)
4134306c97e2SMark Johnston {
4135306c97e2SMark Johnston struct rl_softc *sc;
4136306c97e2SMark Johnston int error;
4137306c97e2SMark Johnston
4138306c97e2SMark Johnston sc = if_getsoftc(ifp);
4139306c97e2SMark Johnston if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0 ||
4140306c97e2SMark Johnston (sc->rl_flags & RL_FLAG_LINK) == 0)
4141306c97e2SMark Johnston return (EBUSY);
4142306c97e2SMark Johnston
4143306c97e2SMark Johnston re_txeof(sc);
4144306c97e2SMark Johnston error = re_rxeof(sc, NULL);
4145306c97e2SMark Johnston if (error != 0 && error != EAGAIN)
4146306c97e2SMark Johnston return (error);
4147306c97e2SMark Johnston return (0);
4148306c97e2SMark Johnston }
41497790c8c1SConrad Meyer #endif /* DEBUGNET */
4150