1c8befdd5SWarner Losh /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
4c8befdd5SWarner Losh * Copyright (c) 1997, 1998, 1999
5c8befdd5SWarner Losh * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
6c8befdd5SWarner Losh *
7c8befdd5SWarner Losh * Redistribution and use in source and binary forms, with or without
8c8befdd5SWarner Losh * modification, are permitted provided that the following conditions
9c8befdd5SWarner Losh * are met:
10c8befdd5SWarner Losh * 1. Redistributions of source code must retain the above copyright
11c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer.
12c8befdd5SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
13c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer in the
14c8befdd5SWarner Losh * documentation and/or other materials provided with the distribution.
15c8befdd5SWarner Losh * 3. All advertising materials mentioning features or use of this software
16c8befdd5SWarner Losh * must display the following acknowledgement:
17c8befdd5SWarner Losh * This product includes software developed by Bill Paul.
18c8befdd5SWarner Losh * 4. Neither the name of the author nor the names of any co-contributors
19c8befdd5SWarner Losh * may be used to endorse or promote products derived from this software
20c8befdd5SWarner Losh * without specific prior written permission.
21c8befdd5SWarner Losh *
22c8befdd5SWarner Losh * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23c8befdd5SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24c8befdd5SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25c8befdd5SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26c8befdd5SWarner Losh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27c8befdd5SWarner Losh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28c8befdd5SWarner Losh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29c8befdd5SWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30c8befdd5SWarner Losh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31c8befdd5SWarner Losh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32c8befdd5SWarner Losh * THE POSSIBILITY OF SUCH DAMAGE.
33c8befdd5SWarner Losh */
34c8befdd5SWarner Losh
35c8befdd5SWarner Losh #include <sys/cdefs.h>
36c8befdd5SWarner Losh #ifdef HAVE_KERNEL_OPTION_HEADERS
37c8befdd5SWarner Losh #include "opt_device_polling.h"
38c8befdd5SWarner Losh #endif
39c8befdd5SWarner Losh
40c8befdd5SWarner Losh #include <sys/param.h>
41c8befdd5SWarner Losh #include <sys/systm.h>
42a1b2c209SPyun YongHyeon #include <sys/bus.h>
43a1b2c209SPyun YongHyeon #include <sys/endian.h>
44c8befdd5SWarner Losh #include <sys/kernel.h>
45a1b2c209SPyun YongHyeon #include <sys/lock.h>
46a1b2c209SPyun YongHyeon #include <sys/malloc.h>
47a1b2c209SPyun YongHyeon #include <sys/mbuf.h>
48c8befdd5SWarner Losh #include <sys/module.h>
49a1b2c209SPyun YongHyeon #include <sys/rman.h>
50c8befdd5SWarner Losh #include <sys/socket.h>
51a1b2c209SPyun YongHyeon #include <sys/sockio.h>
52c8befdd5SWarner Losh #include <sys/sysctl.h>
53c8befdd5SWarner Losh
54a1b2c209SPyun YongHyeon #include <net/bpf.h>
55c8befdd5SWarner Losh #include <net/if.h>
5676039bc8SGleb Smirnoff #include <net/if_var.h>
57c8befdd5SWarner Losh #include <net/if_arp.h>
58c8befdd5SWarner Losh #include <net/ethernet.h>
59c8befdd5SWarner Losh #include <net/if_dl.h>
60c8befdd5SWarner Losh #include <net/if_media.h>
61c8befdd5SWarner Losh #include <net/if_types.h>
62c8befdd5SWarner Losh #include <net/if_vlan_var.h>
63c8befdd5SWarner Losh
64c8befdd5SWarner Losh #include <machine/bus.h>
65c8befdd5SWarner Losh #include <machine/resource.h>
66c8befdd5SWarner Losh
67c8befdd5SWarner Losh #include <dev/mii/mii.h>
688c1093fcSMarius Strobl #include <dev/mii/mii_bitbang.h>
69c8befdd5SWarner Losh #include <dev/mii/miivar.h>
70c8befdd5SWarner Losh
71c8befdd5SWarner Losh #include <dev/pci/pcireg.h>
72c8befdd5SWarner Losh #include <dev/pci/pcivar.h>
73c8befdd5SWarner Losh
74a1b2c209SPyun YongHyeon #include <dev/ste/if_stereg.h>
75a1b2c209SPyun YongHyeon
76c8befdd5SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */
77c8befdd5SWarner Losh #include "miibus_if.h"
78c8befdd5SWarner Losh
79c8befdd5SWarner Losh MODULE_DEPEND(ste, pci, 1, 1, 1);
80c8befdd5SWarner Losh MODULE_DEPEND(ste, ether, 1, 1, 1);
81c8befdd5SWarner Losh MODULE_DEPEND(ste, miibus, 1, 1, 1);
82c8befdd5SWarner Losh
8381598b3eSPyun YongHyeon /* Define to show Tx error status. */
8481598b3eSPyun YongHyeon #define STE_SHOW_TXERRORS
8581598b3eSPyun YongHyeon
86c8befdd5SWarner Losh /*
87c8befdd5SWarner Losh * Various supported device vendors/types and their names.
88c8befdd5SWarner Losh */
8929658c96SDimitry Andric static const struct ste_type ste_devs[] = {
90c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_1, "Sundance ST201 10/100BaseTX" },
91c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_2, "Sundance ST201 10/100BaseTX" },
92c8befdd5SWarner Losh { DL_VENDORID, DL_DEVICEID_DL10050, "D-Link DL10050 10/100BaseTX" },
93c8befdd5SWarner Losh { 0, 0, NULL }
94c8befdd5SWarner Losh };
95c8befdd5SWarner Losh
96c8befdd5SWarner Losh static int ste_attach(device_t);
97c8befdd5SWarner Losh static int ste_detach(device_t);
98084dc54bSPyun YongHyeon static int ste_probe(device_t);
99b4c170e1SPyun YongHyeon static int ste_resume(device_t);
100c8befdd5SWarner Losh static int ste_shutdown(device_t);
101b4c170e1SPyun YongHyeon static int ste_suspend(device_t);
102084dc54bSPyun YongHyeon
103a1b2c209SPyun YongHyeon static int ste_dma_alloc(struct ste_softc *);
104a1b2c209SPyun YongHyeon static void ste_dma_free(struct ste_softc *);
105a1b2c209SPyun YongHyeon static void ste_dmamap_cb(void *, bus_dma_segment_t *, int, int);
106084dc54bSPyun YongHyeon static int ste_eeprom_wait(struct ste_softc *);
107a1b2c209SPyun YongHyeon static int ste_encap(struct ste_softc *, struct mbuf **,
108a1b2c209SPyun YongHyeon struct ste_chain *);
1096712df3aSJustin Hibbits static int ste_ifmedia_upd(if_t);
1106712df3aSJustin Hibbits static void ste_ifmedia_sts(if_t, struct ifmediareq *);
111084dc54bSPyun YongHyeon static void ste_init(void *);
112084dc54bSPyun YongHyeon static void ste_init_locked(struct ste_softc *);
113c8befdd5SWarner Losh static int ste_init_rx_list(struct ste_softc *);
114c8befdd5SWarner Losh static void ste_init_tx_list(struct ste_softc *);
115084dc54bSPyun YongHyeon static void ste_intr(void *);
1166712df3aSJustin Hibbits static int ste_ioctl(if_t, u_long, caddr_t);
1178c1093fcSMarius Strobl static uint32_t ste_mii_bitbang_read(device_t);
1188c1093fcSMarius Strobl static void ste_mii_bitbang_write(device_t, uint32_t);
119084dc54bSPyun YongHyeon static int ste_miibus_readreg(device_t, int, int);
120084dc54bSPyun YongHyeon static void ste_miibus_statchg(device_t);
121084dc54bSPyun YongHyeon static int ste_miibus_writereg(device_t, int, int, int);
122a1b2c209SPyun YongHyeon static int ste_newbuf(struct ste_softc *, struct ste_chain_onefrag *);
123fcd8385eSPyun YongHyeon static int ste_read_eeprom(struct ste_softc *, uint16_t *, int, int);
124084dc54bSPyun YongHyeon static void ste_reset(struct ste_softc *);
12581598b3eSPyun YongHyeon static void ste_restart_tx(struct ste_softc *);
126a1b2c209SPyun YongHyeon static int ste_rxeof(struct ste_softc *, int);
127931ec15aSPyun YongHyeon static void ste_rxfilter(struct ste_softc *);
128b4c170e1SPyun YongHyeon static void ste_setwol(struct ste_softc *);
1296712df3aSJustin Hibbits static void ste_start(if_t);
1306712df3aSJustin Hibbits static void ste_start_locked(if_t);
1318657caa6SPyun YongHyeon static void ste_stats_clear(struct ste_softc *);
13210f695eeSPyun YongHyeon static void ste_stats_update(struct ste_softc *);
133084dc54bSPyun YongHyeon static void ste_stop(struct ste_softc *);
1348657caa6SPyun YongHyeon static void ste_sysctl_node(struct ste_softc *);
13510f695eeSPyun YongHyeon static void ste_tick(void *);
136084dc54bSPyun YongHyeon static void ste_txeoc(struct ste_softc *);
137084dc54bSPyun YongHyeon static void ste_txeof(struct ste_softc *);
138084dc54bSPyun YongHyeon static void ste_wait(struct ste_softc *);
139084dc54bSPyun YongHyeon static void ste_watchdog(struct ste_softc *);
140c8befdd5SWarner Losh
1418c1093fcSMarius Strobl /*
1428c1093fcSMarius Strobl * MII bit-bang glue
1438c1093fcSMarius Strobl */
1448c1093fcSMarius Strobl static const struct mii_bitbang_ops ste_mii_bitbang_ops = {
1458c1093fcSMarius Strobl ste_mii_bitbang_read,
1468c1093fcSMarius Strobl ste_mii_bitbang_write,
1478c1093fcSMarius Strobl {
1488c1093fcSMarius Strobl STE_PHYCTL_MDATA, /* MII_BIT_MDO */
1498c1093fcSMarius Strobl STE_PHYCTL_MDATA, /* MII_BIT_MDI */
1508c1093fcSMarius Strobl STE_PHYCTL_MCLK, /* MII_BIT_MDC */
1518c1093fcSMarius Strobl STE_PHYCTL_MDIR, /* MII_BIT_DIR_HOST_PHY */
1528c1093fcSMarius Strobl 0, /* MII_BIT_DIR_PHY_HOST */
1538c1093fcSMarius Strobl }
1548c1093fcSMarius Strobl };
1558c1093fcSMarius Strobl
156c8befdd5SWarner Losh static device_method_t ste_methods[] = {
157c8befdd5SWarner Losh /* Device interface */
158c8befdd5SWarner Losh DEVMETHOD(device_probe, ste_probe),
159c8befdd5SWarner Losh DEVMETHOD(device_attach, ste_attach),
160c8befdd5SWarner Losh DEVMETHOD(device_detach, ste_detach),
161c8befdd5SWarner Losh DEVMETHOD(device_shutdown, ste_shutdown),
162b4c170e1SPyun YongHyeon DEVMETHOD(device_suspend, ste_suspend),
163b4c170e1SPyun YongHyeon DEVMETHOD(device_resume, ste_resume),
164c8befdd5SWarner Losh
165c8befdd5SWarner Losh /* MII interface */
166c8befdd5SWarner Losh DEVMETHOD(miibus_readreg, ste_miibus_readreg),
167c8befdd5SWarner Losh DEVMETHOD(miibus_writereg, ste_miibus_writereg),
168c8befdd5SWarner Losh DEVMETHOD(miibus_statchg, ste_miibus_statchg),
169c8befdd5SWarner Losh
1704b7ec270SMarius Strobl DEVMETHOD_END
171c8befdd5SWarner Losh };
172c8befdd5SWarner Losh
173c8befdd5SWarner Losh static driver_t ste_driver = {
174c8befdd5SWarner Losh "ste",
175c8befdd5SWarner Losh ste_methods,
176c8befdd5SWarner Losh sizeof(struct ste_softc)
177c8befdd5SWarner Losh };
178c8befdd5SWarner Losh
179b04a8a69SJohn Baldwin DRIVER_MODULE(ste, pci, ste_driver, 0, 0);
1803e38757dSJohn Baldwin DRIVER_MODULE(miibus, ste, miibus_driver, 0, 0);
181c8befdd5SWarner Losh
182c8befdd5SWarner Losh #define STE_SETBIT4(sc, reg, x) \
183c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x))
184c8befdd5SWarner Losh
185c8befdd5SWarner Losh #define STE_CLRBIT4(sc, reg, x) \
186c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x))
187c8befdd5SWarner Losh
188c8befdd5SWarner Losh #define STE_SETBIT2(sc, reg, x) \
189c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x))
190c8befdd5SWarner Losh
191c8befdd5SWarner Losh #define STE_CLRBIT2(sc, reg, x) \
192c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x))
193c8befdd5SWarner Losh
194c8befdd5SWarner Losh #define STE_SETBIT1(sc, reg, x) \
195c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x))
196c8befdd5SWarner Losh
197c8befdd5SWarner Losh #define STE_CLRBIT1(sc, reg, x) \
198c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x))
199c8befdd5SWarner Losh
2008c1093fcSMarius Strobl /*
2018c1093fcSMarius Strobl * Read the MII serial port for the MII bit-bang module.
2028c1093fcSMarius Strobl */
2038c1093fcSMarius Strobl static uint32_t
ste_mii_bitbang_read(device_t dev)2048c1093fcSMarius Strobl ste_mii_bitbang_read(device_t dev)
2058c1093fcSMarius Strobl {
2068c1093fcSMarius Strobl struct ste_softc *sc;
2078c1093fcSMarius Strobl uint32_t val;
208c8befdd5SWarner Losh
2098c1093fcSMarius Strobl sc = device_get_softc(dev);
2108c1093fcSMarius Strobl
2118c1093fcSMarius Strobl val = CSR_READ_1(sc, STE_PHYCTL);
2128c1093fcSMarius Strobl CSR_BARRIER(sc, STE_PHYCTL, 1,
2138c1093fcSMarius Strobl BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
2148c1093fcSMarius Strobl
2158c1093fcSMarius Strobl return (val);
2168c1093fcSMarius Strobl }
217c8befdd5SWarner Losh
218c8befdd5SWarner Losh /*
2198c1093fcSMarius Strobl * Write the MII serial port for the MII bit-bang module.
220c8befdd5SWarner Losh */
221c8befdd5SWarner Losh static void
ste_mii_bitbang_write(device_t dev,uint32_t val)2228c1093fcSMarius Strobl ste_mii_bitbang_write(device_t dev, uint32_t val)
223c8befdd5SWarner Losh {
2248c1093fcSMarius Strobl struct ste_softc *sc;
225c8befdd5SWarner Losh
2268c1093fcSMarius Strobl sc = device_get_softc(dev);
227c8befdd5SWarner Losh
2288c1093fcSMarius Strobl CSR_WRITE_1(sc, STE_PHYCTL, val);
2298c1093fcSMarius Strobl CSR_BARRIER(sc, STE_PHYCTL, 1,
2308c1093fcSMarius Strobl BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
231c8befdd5SWarner Losh }
232c8befdd5SWarner Losh
233c8befdd5SWarner Losh static int
ste_miibus_readreg(device_t dev,int phy,int reg)23460270842SPyun YongHyeon ste_miibus_readreg(device_t dev, int phy, int reg)
235c8befdd5SWarner Losh {
236c8befdd5SWarner Losh
2378c1093fcSMarius Strobl return (mii_bitbang_readreg(dev, &ste_mii_bitbang_ops, phy, reg));
238c8befdd5SWarner Losh }
239c8befdd5SWarner Losh
240c8befdd5SWarner Losh static int
ste_miibus_writereg(device_t dev,int phy,int reg,int data)24160270842SPyun YongHyeon ste_miibus_writereg(device_t dev, int phy, int reg, int data)
242c8befdd5SWarner Losh {
243c8befdd5SWarner Losh
2448c1093fcSMarius Strobl mii_bitbang_writereg(dev, &ste_mii_bitbang_ops, phy, reg, data);
245c8befdd5SWarner Losh
246c8befdd5SWarner Losh return (0);
247c8befdd5SWarner Losh }
248c8befdd5SWarner Losh
249c8befdd5SWarner Losh static void
ste_miibus_statchg(device_t dev)25060270842SPyun YongHyeon ste_miibus_statchg(device_t dev)
251c8befdd5SWarner Losh {
252c8befdd5SWarner Losh struct ste_softc *sc;
253c8befdd5SWarner Losh struct mii_data *mii;
2546712df3aSJustin Hibbits if_t ifp;
25510f695eeSPyun YongHyeon uint16_t cfg;
256c8befdd5SWarner Losh
257c8befdd5SWarner Losh sc = device_get_softc(dev);
258c8befdd5SWarner Losh
259c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus);
26010f695eeSPyun YongHyeon ifp = sc->ste_ifp;
26110f695eeSPyun YongHyeon if (mii == NULL || ifp == NULL ||
2626712df3aSJustin Hibbits (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
26310f695eeSPyun YongHyeon return;
264c8befdd5SWarner Losh
26510f695eeSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK;
26610f695eeSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
26710f695eeSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) {
26810f695eeSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) {
26910f695eeSPyun YongHyeon case IFM_10_T:
27010f695eeSPyun YongHyeon case IFM_100_TX:
27110f695eeSPyun YongHyeon case IFM_100_FX:
27210f695eeSPyun YongHyeon case IFM_100_T4:
27310f695eeSPyun YongHyeon sc->ste_flags |= STE_FLAG_LINK;
27410f695eeSPyun YongHyeon default:
27510f695eeSPyun YongHyeon break;
27610f695eeSPyun YongHyeon }
27710f695eeSPyun YongHyeon }
27810f695eeSPyun YongHyeon
27910f695eeSPyun YongHyeon /* Program MACs with resolved speed/duplex/flow-control. */
28010f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) != 0) {
28110f695eeSPyun YongHyeon cfg = CSR_READ_2(sc, STE_MACCTL0);
28210f695eeSPyun YongHyeon cfg &= ~(STE_MACCTL0_FLOWCTL_ENABLE | STE_MACCTL0_FULLDUPLEX);
28310f695eeSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
28410f695eeSPyun YongHyeon /*
28510f695eeSPyun YongHyeon * ST201 data sheet says driver should enable receiving
28610f695eeSPyun YongHyeon * MAC control frames bit of receive mode register to
28710f695eeSPyun YongHyeon * receive flow-control frames but the register has no
28810f695eeSPyun YongHyeon * such bits. In addition the controller has no ability
28910f695eeSPyun YongHyeon * to send pause frames so it should be handled in
29010f695eeSPyun YongHyeon * driver. Implementing pause timer handling in driver
29110f695eeSPyun YongHyeon * layer is not trivial, so don't enable flow-control
29210f695eeSPyun YongHyeon * here.
29310f695eeSPyun YongHyeon */
29410f695eeSPyun YongHyeon cfg |= STE_MACCTL0_FULLDUPLEX;
29510f695eeSPyun YongHyeon }
29610f695eeSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL0, cfg);
297c8befdd5SWarner Losh }
298c8befdd5SWarner Losh }
299c8befdd5SWarner Losh
300c8befdd5SWarner Losh static int
ste_ifmedia_upd(if_t ifp)3016712df3aSJustin Hibbits ste_ifmedia_upd(if_t ifp)
302c8befdd5SWarner Losh {
303c8befdd5SWarner Losh struct ste_softc *sc;
304bfe051bdSPyun YongHyeon struct mii_data *mii;
305bfe051bdSPyun YongHyeon struct mii_softc *miisc;
306bfe051bdSPyun YongHyeon int error;
307c8befdd5SWarner Losh
3086712df3aSJustin Hibbits sc = if_getsoftc(ifp);
309c8befdd5SWarner Losh STE_LOCK(sc);
310c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus);
311c8befdd5SWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
3123fcb7a53SMarius Strobl PHY_RESET(miisc);
313bfe051bdSPyun YongHyeon error = mii_mediachg(mii);
314bfe051bdSPyun YongHyeon STE_UNLOCK(sc);
315bfe051bdSPyun YongHyeon
316bfe051bdSPyun YongHyeon return (error);
317c8befdd5SWarner Losh }
318c8befdd5SWarner Losh
319c8befdd5SWarner Losh static void
ste_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)3206712df3aSJustin Hibbits ste_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
321c8befdd5SWarner Losh {
322c8befdd5SWarner Losh struct ste_softc *sc;
323c8befdd5SWarner Losh struct mii_data *mii;
324c8befdd5SWarner Losh
3256712df3aSJustin Hibbits sc = if_getsoftc(ifp);
326c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus);
327c8befdd5SWarner Losh
328c8befdd5SWarner Losh STE_LOCK(sc);
3296712df3aSJustin Hibbits if ((if_getflags(ifp) & IFF_UP) == 0) {
3305b7e3118SPyun YongHyeon STE_UNLOCK(sc);
3315b7e3118SPyun YongHyeon return;
3325b7e3118SPyun YongHyeon }
333c8befdd5SWarner Losh mii_pollstat(mii);
334c8befdd5SWarner Losh ifmr->ifm_active = mii->mii_media_active;
335c8befdd5SWarner Losh ifmr->ifm_status = mii->mii_media_status;
336c8befdd5SWarner Losh STE_UNLOCK(sc);
337c8befdd5SWarner Losh }
338c8befdd5SWarner Losh
339c8befdd5SWarner Losh static void
ste_wait(struct ste_softc * sc)34060270842SPyun YongHyeon ste_wait(struct ste_softc *sc)
341c8befdd5SWarner Losh {
34242306cb0SPyun YongHyeon int i;
343c8befdd5SWarner Losh
344c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) {
345c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG))
346c8befdd5SWarner Losh break;
3471bf71544SPyun YongHyeon DELAY(1);
348c8befdd5SWarner Losh }
349c8befdd5SWarner Losh
350c8befdd5SWarner Losh if (i == STE_TIMEOUT)
351c8befdd5SWarner Losh device_printf(sc->ste_dev, "command never completed!\n");
352c8befdd5SWarner Losh }
353c8befdd5SWarner Losh
354c8befdd5SWarner Losh /*
355c8befdd5SWarner Losh * The EEPROM is slow: give it time to come ready after issuing
356c8befdd5SWarner Losh * it a command.
357c8befdd5SWarner Losh */
358c8befdd5SWarner Losh static int
ste_eeprom_wait(struct ste_softc * sc)35960270842SPyun YongHyeon ste_eeprom_wait(struct ste_softc *sc)
360c8befdd5SWarner Losh {
361c8befdd5SWarner Losh int i;
362c8befdd5SWarner Losh
363c8befdd5SWarner Losh DELAY(1000);
364c8befdd5SWarner Losh
365c8befdd5SWarner Losh for (i = 0; i < 100; i++) {
366c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY)
367c8befdd5SWarner Losh DELAY(1000);
368c8befdd5SWarner Losh else
369c8befdd5SWarner Losh break;
370c8befdd5SWarner Losh }
371c8befdd5SWarner Losh
372c8befdd5SWarner Losh if (i == 100) {
373c8befdd5SWarner Losh device_printf(sc->ste_dev, "eeprom failed to come ready\n");
374c8befdd5SWarner Losh return (1);
375c8befdd5SWarner Losh }
376c8befdd5SWarner Losh
377c8befdd5SWarner Losh return (0);
378c8befdd5SWarner Losh }
379c8befdd5SWarner Losh
380c8befdd5SWarner Losh /*
381c8befdd5SWarner Losh * Read a sequence of words from the EEPROM. Note that ethernet address
382c8befdd5SWarner Losh * data is stored in the EEPROM in network byte order.
383c8befdd5SWarner Losh */
384c8befdd5SWarner Losh static int
ste_read_eeprom(struct ste_softc * sc,uint16_t * dest,int off,int cnt)385fcd8385eSPyun YongHyeon ste_read_eeprom(struct ste_softc *sc, uint16_t *dest, int off, int cnt)
386c8befdd5SWarner Losh {
387c8befdd5SWarner Losh int err = 0, i;
388c8befdd5SWarner Losh
389c8befdd5SWarner Losh if (ste_eeprom_wait(sc))
390c8befdd5SWarner Losh return (1);
391c8befdd5SWarner Losh
392c8befdd5SWarner Losh for (i = 0; i < cnt; i++) {
393c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i));
394c8befdd5SWarner Losh err = ste_eeprom_wait(sc);
395c8befdd5SWarner Losh if (err)
396c8befdd5SWarner Losh break;
397fcd8385eSPyun YongHyeon *dest = le16toh(CSR_READ_2(sc, STE_EEPROM_DATA));
398fcd8385eSPyun YongHyeon dest++;
399c8befdd5SWarner Losh }
400c8befdd5SWarner Losh
401c8befdd5SWarner Losh return (err ? 1 : 0);
402c8befdd5SWarner Losh }
403c8befdd5SWarner Losh
404f2921d4cSGleb Smirnoff static u_int
ste_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)405f2921d4cSGleb Smirnoff ste_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
406f2921d4cSGleb Smirnoff {
407f2921d4cSGleb Smirnoff uint32_t *hashes = arg;
408f2921d4cSGleb Smirnoff int h;
409f2921d4cSGleb Smirnoff
410f2921d4cSGleb Smirnoff h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) & 0x3F;
411f2921d4cSGleb Smirnoff if (h < 32)
412f2921d4cSGleb Smirnoff hashes[0] |= (1 << h);
413f2921d4cSGleb Smirnoff else
414f2921d4cSGleb Smirnoff hashes[1] |= (1 << (h - 32));
415f2921d4cSGleb Smirnoff
416f2921d4cSGleb Smirnoff return (1);
417f2921d4cSGleb Smirnoff }
418f2921d4cSGleb Smirnoff
419c8befdd5SWarner Losh static void
ste_rxfilter(struct ste_softc * sc)420931ec15aSPyun YongHyeon ste_rxfilter(struct ste_softc *sc)
421c8befdd5SWarner Losh {
4226712df3aSJustin Hibbits if_t ifp;
423f2632c3bSPyun YongHyeon uint32_t hashes[2] = { 0, 0 };
424931ec15aSPyun YongHyeon uint8_t rxcfg;
425c8befdd5SWarner Losh
426931ec15aSPyun YongHyeon STE_LOCK_ASSERT(sc);
427931ec15aSPyun YongHyeon
428c8befdd5SWarner Losh ifp = sc->ste_ifp;
429931ec15aSPyun YongHyeon rxcfg = CSR_READ_1(sc, STE_RX_MODE);
430931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_UNICAST;
431931ec15aSPyun YongHyeon rxcfg &= ~(STE_RXMODE_ALLMULTI | STE_RXMODE_MULTIHASH |
432931ec15aSPyun YongHyeon STE_RXMODE_BROADCAST | STE_RXMODE_PROMISC);
4336712df3aSJustin Hibbits if (if_getflags(ifp) & IFF_BROADCAST)
434931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_BROADCAST;
4356712df3aSJustin Hibbits if ((if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
4366712df3aSJustin Hibbits if ((if_getflags(ifp) & IFF_ALLMULTI) != 0)
437931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_ALLMULTI;
4386712df3aSJustin Hibbits if ((if_getflags(ifp) & IFF_PROMISC) != 0)
439931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_PROMISC;
440931ec15aSPyun YongHyeon goto chipit;
441c8befdd5SWarner Losh }
442c8befdd5SWarner Losh
443931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_MULTIHASH;
444931ec15aSPyun YongHyeon /* Now program new ones. */
445f2921d4cSGleb Smirnoff if_foreach_llmaddr(ifp, ste_hash_maddr, hashes);
446c8befdd5SWarner Losh
447931ec15aSPyun YongHyeon chipit:
448c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF);
449c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF);
450c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF);
451c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF);
452931ec15aSPyun YongHyeon CSR_WRITE_1(sc, STE_RX_MODE, rxcfg);
453931ec15aSPyun YongHyeon CSR_READ_1(sc, STE_RX_MODE);
454c8befdd5SWarner Losh }
455c8befdd5SWarner Losh
456c8befdd5SWarner Losh #ifdef DEVICE_POLLING
457c8befdd5SWarner Losh static poll_handler_t ste_poll, ste_poll_locked;
458c8befdd5SWarner Losh
4591abcdbd1SAttilio Rao static int
ste_poll(if_t ifp,enum poll_cmd cmd,int count)4606712df3aSJustin Hibbits ste_poll(if_t ifp, enum poll_cmd cmd, int count)
461c8befdd5SWarner Losh {
4626712df3aSJustin Hibbits struct ste_softc *sc = if_getsoftc(ifp);
4631abcdbd1SAttilio Rao int rx_npkts = 0;
464c8befdd5SWarner Losh
465c8befdd5SWarner Losh STE_LOCK(sc);
4666712df3aSJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
4671abcdbd1SAttilio Rao rx_npkts = ste_poll_locked(ifp, cmd, count);
468c8befdd5SWarner Losh STE_UNLOCK(sc);
4691abcdbd1SAttilio Rao return (rx_npkts);
470c8befdd5SWarner Losh }
471c8befdd5SWarner Losh
4721abcdbd1SAttilio Rao static int
ste_poll_locked(if_t ifp,enum poll_cmd cmd,int count)4736712df3aSJustin Hibbits ste_poll_locked(if_t ifp, enum poll_cmd cmd, int count)
474c8befdd5SWarner Losh {
4756712df3aSJustin Hibbits struct ste_softc *sc = if_getsoftc(ifp);
4761abcdbd1SAttilio Rao int rx_npkts;
477c8befdd5SWarner Losh
478c8befdd5SWarner Losh STE_LOCK_ASSERT(sc);
479c8befdd5SWarner Losh
480a1b2c209SPyun YongHyeon rx_npkts = ste_rxeof(sc, count);
481c8befdd5SWarner Losh ste_txeof(sc);
48281598b3eSPyun YongHyeon ste_txeoc(sc);
4836712df3aSJustin Hibbits if (!if_sendq_empty(ifp))
484c8befdd5SWarner Losh ste_start_locked(ifp);
485c8befdd5SWarner Losh
486c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) {
48756af54f2SPyun YongHyeon uint16_t status;
488c8befdd5SWarner Losh
489c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK);
490c8befdd5SWarner Losh
49110f695eeSPyun YongHyeon if (status & STE_ISR_STATS_OFLOW)
492c8befdd5SWarner Losh ste_stats_update(sc);
493c8befdd5SWarner Losh
49455d7003eSPyun YongHyeon if (status & STE_ISR_HOSTERR) {
4956712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
496c8befdd5SWarner Losh ste_init_locked(sc);
497c8befdd5SWarner Losh }
49855d7003eSPyun YongHyeon }
4991abcdbd1SAttilio Rao return (rx_npkts);
500c8befdd5SWarner Losh }
501c8befdd5SWarner Losh #endif /* DEVICE_POLLING */
502c8befdd5SWarner Losh
503c8befdd5SWarner Losh static void
ste_intr(void * xsc)50460270842SPyun YongHyeon ste_intr(void *xsc)
505c8befdd5SWarner Losh {
506c8befdd5SWarner Losh struct ste_softc *sc;
5076712df3aSJustin Hibbits if_t ifp;
508fabbaac5SPyun YongHyeon uint16_t intrs, status;
509c8befdd5SWarner Losh
510c8befdd5SWarner Losh sc = xsc;
511c8befdd5SWarner Losh STE_LOCK(sc);
512c8befdd5SWarner Losh ifp = sc->ste_ifp;
513c8befdd5SWarner Losh
514c8befdd5SWarner Losh #ifdef DEVICE_POLLING
5156712df3aSJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING) {
516c8befdd5SWarner Losh STE_UNLOCK(sc);
517c8befdd5SWarner Losh return;
518c8befdd5SWarner Losh }
519c8befdd5SWarner Losh #endif
520fabbaac5SPyun YongHyeon /* Reading STE_ISR_ACK clears STE_IMR register. */
521fabbaac5SPyun YongHyeon status = CSR_READ_2(sc, STE_ISR_ACK);
5226712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
523c8befdd5SWarner Losh STE_UNLOCK(sc);
524c8befdd5SWarner Losh return;
525c8befdd5SWarner Losh }
526c8befdd5SWarner Losh
527fabbaac5SPyun YongHyeon intrs = STE_INTRS;
528fabbaac5SPyun YongHyeon if (status == 0xFFFF || (status & intrs) == 0)
529fabbaac5SPyun YongHyeon goto done;
530c8befdd5SWarner Losh
531fabbaac5SPyun YongHyeon if (sc->ste_int_rx_act > 0) {
532fabbaac5SPyun YongHyeon status &= ~STE_ISR_RX_DMADONE;
533fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE;
534fabbaac5SPyun YongHyeon }
535c8befdd5SWarner Losh
536fabbaac5SPyun YongHyeon if ((status & (STE_ISR_SOFTINTR | STE_ISR_RX_DMADONE)) != 0) {
537a1b2c209SPyun YongHyeon ste_rxeof(sc, -1);
538fabbaac5SPyun YongHyeon /*
539fabbaac5SPyun YongHyeon * The controller has no ability to Rx interrupt
540fabbaac5SPyun YongHyeon * moderation feature. Receiving 64 bytes frames
541fabbaac5SPyun YongHyeon * from wire generates too many interrupts which in
542fabbaac5SPyun YongHyeon * turn make system useless to process other useful
543fabbaac5SPyun YongHyeon * things. Fortunately ST201 supports single shot
544fabbaac5SPyun YongHyeon * timer so use the timer to implement Rx interrupt
545fabbaac5SPyun YongHyeon * moderation in driver. This adds more register
546fabbaac5SPyun YongHyeon * access but it greatly reduces number of Rx
547fabbaac5SPyun YongHyeon * interrupts under high network load.
548fabbaac5SPyun YongHyeon */
5496712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0 &&
550fabbaac5SPyun YongHyeon (sc->ste_int_rx_mod != 0)) {
551fabbaac5SPyun YongHyeon if ((status & STE_ISR_RX_DMADONE) != 0) {
552fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN,
553fabbaac5SPyun YongHyeon STE_TIMER_USECS(sc->ste_int_rx_mod));
554fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE;
555fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 1;
556fabbaac5SPyun YongHyeon } else {
557fabbaac5SPyun YongHyeon intrs |= STE_IMR_RX_DMADONE;
558fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0;
559fabbaac5SPyun YongHyeon }
560fabbaac5SPyun YongHyeon }
561fabbaac5SPyun YongHyeon }
5626712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
563fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DMADONE) != 0)
564c8befdd5SWarner Losh ste_txeof(sc);
565fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DONE) != 0)
566c8befdd5SWarner Losh ste_txeoc(sc);
567fabbaac5SPyun YongHyeon if ((status & STE_ISR_STATS_OFLOW) != 0)
568c8befdd5SWarner Losh ste_stats_update(sc);
569fabbaac5SPyun YongHyeon if ((status & STE_ISR_HOSTERR) != 0) {
5706712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
571fabbaac5SPyun YongHyeon ste_init_locked(sc);
572fabbaac5SPyun YongHyeon STE_UNLOCK(sc);
573fabbaac5SPyun YongHyeon return;
57455d7003eSPyun YongHyeon }
5756712df3aSJustin Hibbits if (!if_sendq_empty(ifp))
576c8befdd5SWarner Losh ste_start_locked(ifp);
577fabbaac5SPyun YongHyeon done:
578fabbaac5SPyun YongHyeon /* Re-enable interrupts */
579fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, intrs);
580fabbaac5SPyun YongHyeon }
581c8befdd5SWarner Losh STE_UNLOCK(sc);
582c8befdd5SWarner Losh }
583c8befdd5SWarner Losh
584c8befdd5SWarner Losh /*
585c8befdd5SWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to
586c8befdd5SWarner Losh * the higher level protocols.
587c8befdd5SWarner Losh */
5881abcdbd1SAttilio Rao static int
ste_rxeof(struct ste_softc * sc,int count)589a1b2c209SPyun YongHyeon ste_rxeof(struct ste_softc *sc, int count)
590c8befdd5SWarner Losh {
591c8befdd5SWarner Losh struct mbuf *m;
5926712df3aSJustin Hibbits if_t ifp;
593c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx;
59456af54f2SPyun YongHyeon uint32_t rxstat;
595a1b2c209SPyun YongHyeon int total_len, rx_npkts;
596c8befdd5SWarner Losh
597c8befdd5SWarner Losh ifp = sc->ste_ifp;
598c8befdd5SWarner Losh
599a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag,
600a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map,
601a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
602c8befdd5SWarner Losh
603c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head;
604a1b2c209SPyun YongHyeon for (rx_npkts = 0; rx_npkts < STE_RX_LIST_CNT; rx_npkts++,
605a1b2c209SPyun YongHyeon cur_rx = cur_rx->ste_next) {
606a1b2c209SPyun YongHyeon rxstat = le32toh(cur_rx->ste_ptr->ste_status);
607a1b2c209SPyun YongHyeon if ((rxstat & STE_RXSTAT_DMADONE) == 0)
608a1b2c209SPyun YongHyeon break;
609a1b2c209SPyun YongHyeon #ifdef DEVICE_POLLING
6106712df3aSJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING) {
611a1b2c209SPyun YongHyeon if (count == 0)
612a1b2c209SPyun YongHyeon break;
613a1b2c209SPyun YongHyeon count--;
614a1b2c209SPyun YongHyeon }
615a1b2c209SPyun YongHyeon #endif
6166712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
617a1b2c209SPyun YongHyeon break;
618c8befdd5SWarner Losh /*
619c8befdd5SWarner Losh * If an error occurs, update stats, clear the
620c8befdd5SWarner Losh * status word and leave the mbuf cluster in place:
621c8befdd5SWarner Losh * it should simply get re-used next time this descriptor
622c8befdd5SWarner Losh * comes up in the ring.
623c8befdd5SWarner Losh */
624c8befdd5SWarner Losh if (rxstat & STE_RXSTAT_FRAME_ERR) {
625c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
626c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0;
627c8befdd5SWarner Losh continue;
628c8befdd5SWarner Losh }
629c8befdd5SWarner Losh
630c8befdd5SWarner Losh /* No errors; receive the packet. */
631c8befdd5SWarner Losh m = cur_rx->ste_mbuf;
632a1b2c209SPyun YongHyeon total_len = STE_RX_BYTES(rxstat);
633c8befdd5SWarner Losh
634c8befdd5SWarner Losh /*
635c8befdd5SWarner Losh * Try to conjure up a new mbuf cluster. If that
636c8befdd5SWarner Losh * fails, it means we have an out of memory condition and
637c8befdd5SWarner Losh * should leave the buffer in place and continue. This will
638c8befdd5SWarner Losh * result in a lost packet, but there's little else we
639c8befdd5SWarner Losh * can do in this situation.
640c8befdd5SWarner Losh */
641a1b2c209SPyun YongHyeon if (ste_newbuf(sc, cur_rx) != 0) {
642c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
643c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0;
644c8befdd5SWarner Losh continue;
645c8befdd5SWarner Losh }
646c8befdd5SWarner Losh
647c8befdd5SWarner Losh m->m_pkthdr.rcvif = ifp;
648c8befdd5SWarner Losh m->m_pkthdr.len = m->m_len = total_len;
649c8befdd5SWarner Losh
650c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
651c8befdd5SWarner Losh STE_UNLOCK(sc);
6526712df3aSJustin Hibbits if_input(ifp, m);
653c8befdd5SWarner Losh STE_LOCK(sc);
654a1b2c209SPyun YongHyeon }
655c8befdd5SWarner Losh
656a1b2c209SPyun YongHyeon if (rx_npkts > 0) {
657a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_head = cur_rx;
658a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag,
659a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map,
660a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
661c8befdd5SWarner Losh }
662c8befdd5SWarner Losh
6631abcdbd1SAttilio Rao return (rx_npkts);
664c8befdd5SWarner Losh }
665c8befdd5SWarner Losh
666c8befdd5SWarner Losh static void
ste_txeoc(struct ste_softc * sc)66760270842SPyun YongHyeon ste_txeoc(struct ste_softc *sc)
668c8befdd5SWarner Losh {
66981598b3eSPyun YongHyeon uint16_t txstat;
6706712df3aSJustin Hibbits if_t ifp;
67181598b3eSPyun YongHyeon
67281598b3eSPyun YongHyeon STE_LOCK_ASSERT(sc);
673c8befdd5SWarner Losh
674c8befdd5SWarner Losh ifp = sc->ste_ifp;
675c8befdd5SWarner Losh
67681598b3eSPyun YongHyeon /*
67781598b3eSPyun YongHyeon * STE_TX_STATUS register implements a queue of up to 31
67881598b3eSPyun YongHyeon * transmit status byte. Writing an arbitrary value to the
67981598b3eSPyun YongHyeon * register will advance the queue to the next transmit
68081598b3eSPyun YongHyeon * status byte. This means if driver does not read
68181598b3eSPyun YongHyeon * STE_TX_STATUS register after completing sending more
68281598b3eSPyun YongHyeon * than 31 frames the controller would be stalled so driver
68381598b3eSPyun YongHyeon * should re-wake the Tx MAC. This is the most severe
68481598b3eSPyun YongHyeon * limitation of ST201 based controller.
68581598b3eSPyun YongHyeon */
68681598b3eSPyun YongHyeon for (;;) {
68781598b3eSPyun YongHyeon txstat = CSR_READ_2(sc, STE_TX_STATUS);
68881598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_TXDONE) == 0)
68981598b3eSPyun YongHyeon break;
69081598b3eSPyun YongHyeon if ((txstat & (STE_TXSTATUS_UNDERRUN |
69181598b3eSPyun YongHyeon STE_TXSTATUS_EXCESSCOLLS | STE_TXSTATUS_RECLAIMERR |
69281598b3eSPyun YongHyeon STE_TXSTATUS_STATSOFLOW)) != 0) {
693c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
69481598b3eSPyun YongHyeon #ifdef STE_SHOW_TXERRORS
69581598b3eSPyun YongHyeon device_printf(sc->ste_dev, "TX error : 0x%b\n",
69681598b3eSPyun YongHyeon txstat & 0xFF, STE_ERR_BITS);
69781598b3eSPyun YongHyeon #endif
69881598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_UNDERRUN) != 0 &&
699c8befdd5SWarner Losh sc->ste_tx_thresh < STE_PACKET_SIZE) {
700c8befdd5SWarner Losh sc->ste_tx_thresh += STE_MIN_FRAMELEN;
70181598b3eSPyun YongHyeon if (sc->ste_tx_thresh > STE_PACKET_SIZE)
70281598b3eSPyun YongHyeon sc->ste_tx_thresh = STE_PACKET_SIZE;
703c8befdd5SWarner Losh device_printf(sc->ste_dev,
70481598b3eSPyun YongHyeon "TX underrun, increasing TX"
705c8befdd5SWarner Losh " start threshold to %d bytes\n",
706c8befdd5SWarner Losh sc->ste_tx_thresh);
70781598b3eSPyun YongHyeon /* Make sure to disable active DMA cycles. */
70881598b3eSPyun YongHyeon STE_SETBIT4(sc, STE_DMACTL,
70981598b3eSPyun YongHyeon STE_DMACTL_TXDMA_STALL);
71081598b3eSPyun YongHyeon ste_wait(sc);
7116712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
712c8befdd5SWarner Losh ste_init_locked(sc);
71381598b3eSPyun YongHyeon break;
71481598b3eSPyun YongHyeon }
71581598b3eSPyun YongHyeon /* Restart Tx. */
71681598b3eSPyun YongHyeon ste_restart_tx(sc);
71781598b3eSPyun YongHyeon }
71881598b3eSPyun YongHyeon /*
71981598b3eSPyun YongHyeon * Advance to next status and ACK TxComplete
72081598b3eSPyun YongHyeon * interrupt. ST201 data sheet was wrong here, to
72181598b3eSPyun YongHyeon * get next Tx status, we have to write both
72281598b3eSPyun YongHyeon * STE_TX_STATUS and STE_TX_FRAMEID register.
72381598b3eSPyun YongHyeon * Otherwise controller returns the same status
72481598b3eSPyun YongHyeon * as well as not acknowledge Tx completion
72581598b3eSPyun YongHyeon * interrupt.
72681598b3eSPyun YongHyeon */
727c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STATUS, txstat);
728c8befdd5SWarner Losh }
729c8befdd5SWarner Losh }
730c8befdd5SWarner Losh
731c8befdd5SWarner Losh static void
ste_tick(void * arg)73210f695eeSPyun YongHyeon ste_tick(void *arg)
73310f695eeSPyun YongHyeon {
73410f695eeSPyun YongHyeon struct ste_softc *sc;
73510f695eeSPyun YongHyeon struct mii_data *mii;
73610f695eeSPyun YongHyeon
73710f695eeSPyun YongHyeon sc = (struct ste_softc *)arg;
73810f695eeSPyun YongHyeon
73910f695eeSPyun YongHyeon STE_LOCK_ASSERT(sc);
74010f695eeSPyun YongHyeon
74110f695eeSPyun YongHyeon mii = device_get_softc(sc->ste_miibus);
74210f695eeSPyun YongHyeon mii_tick(mii);
74310f695eeSPyun YongHyeon /*
74410f695eeSPyun YongHyeon * ukphy(4) does not seem to generate CB that reports
74510f695eeSPyun YongHyeon * resolved link state so if we know we lost a link,
74610f695eeSPyun YongHyeon * explicitly check the link state.
74710f695eeSPyun YongHyeon */
74810f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) == 0)
74910f695eeSPyun YongHyeon ste_miibus_statchg(sc->ste_dev);
750ae49e7a6SPyun YongHyeon /*
751ae49e7a6SPyun YongHyeon * Because we are not generating Tx completion
752ae49e7a6SPyun YongHyeon * interrupt for every frame, reclaim transmitted
753ae49e7a6SPyun YongHyeon * buffers here.
754ae49e7a6SPyun YongHyeon */
755ae49e7a6SPyun YongHyeon ste_txeof(sc);
756ae49e7a6SPyun YongHyeon ste_txeoc(sc);
75710f695eeSPyun YongHyeon ste_stats_update(sc);
75810f695eeSPyun YongHyeon ste_watchdog(sc);
75910f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc);
76010f695eeSPyun YongHyeon }
76110f695eeSPyun YongHyeon
76210f695eeSPyun YongHyeon static void
ste_txeof(struct ste_softc * sc)76360270842SPyun YongHyeon ste_txeof(struct ste_softc *sc)
764c8befdd5SWarner Losh {
7656712df3aSJustin Hibbits if_t ifp;
766f2632c3bSPyun YongHyeon struct ste_chain *cur_tx;
767a1b2c209SPyun YongHyeon uint32_t txstat;
768c8befdd5SWarner Losh int idx;
769c8befdd5SWarner Losh
770a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc);
771c8befdd5SWarner Losh
772a1b2c209SPyun YongHyeon ifp = sc->ste_ifp;
773c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_cons;
774a1b2c209SPyun YongHyeon if (idx == sc->ste_cdata.ste_tx_prod)
775a1b2c209SPyun YongHyeon return;
776a1b2c209SPyun YongHyeon
777a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag,
778a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map,
779a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
780a1b2c209SPyun YongHyeon
781c8befdd5SWarner Losh while (idx != sc->ste_cdata.ste_tx_prod) {
782c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx];
783a1b2c209SPyun YongHyeon txstat = le32toh(cur_tx->ste_ptr->ste_ctl);
784a1b2c209SPyun YongHyeon if ((txstat & STE_TXCTL_DMADONE) == 0)
785c8befdd5SWarner Losh break;
786a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map,
787a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTWRITE);
788a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map);
789a1b2c209SPyun YongHyeon KASSERT(cur_tx->ste_mbuf != NULL,
790a1b2c209SPyun YongHyeon ("%s: freeing NULL mbuf!\n", __func__));
791c8befdd5SWarner Losh m_freem(cur_tx->ste_mbuf);
792c8befdd5SWarner Losh cur_tx->ste_mbuf = NULL;
7936712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
794c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
795a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt--;
796c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT);
797c8befdd5SWarner Losh }
798c8befdd5SWarner Losh
799c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons = idx;
800a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == 0)
8017cf545d0SJohn Baldwin sc->ste_timer = 0;
802c8befdd5SWarner Losh }
803c8befdd5SWarner Losh
804c8befdd5SWarner Losh static void
ste_stats_clear(struct ste_softc * sc)8058657caa6SPyun YongHyeon ste_stats_clear(struct ste_softc *sc)
8068657caa6SPyun YongHyeon {
8078657caa6SPyun YongHyeon
8088657caa6SPyun YongHyeon STE_LOCK_ASSERT(sc);
8098657caa6SPyun YongHyeon
8108657caa6SPyun YongHyeon /* Rx stats. */
8118657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO);
8128657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI);
8138657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_FRAMES);
8148657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_BCAST);
8158657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_MCAST);
8168657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_LOST);
8178657caa6SPyun YongHyeon /* Tx stats. */
8188657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO);
8198657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI);
8208657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_FRAMES);
8218657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_BCAST);
8228657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_MCAST);
8238657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_CARRIER_ERR);
8248657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_SINGLE_COLLS);
8258657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_MULTI_COLLS);
8268657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_LATE_COLLS);
8278657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_DEFER);
8288657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_EXDEFER);
8298657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_ABORT);
8308657caa6SPyun YongHyeon }
8318657caa6SPyun YongHyeon
8328657caa6SPyun YongHyeon static void
ste_stats_update(struct ste_softc * sc)83310f695eeSPyun YongHyeon ste_stats_update(struct ste_softc *sc)
834c8befdd5SWarner Losh {
8356712df3aSJustin Hibbits if_t ifp;
8368657caa6SPyun YongHyeon struct ste_hw_stats *stats;
8378657caa6SPyun YongHyeon uint32_t val;
838c8befdd5SWarner Losh
839c8befdd5SWarner Losh STE_LOCK_ASSERT(sc);
840c8befdd5SWarner Losh
841c8befdd5SWarner Losh ifp = sc->ste_ifp;
8428657caa6SPyun YongHyeon stats = &sc->ste_stats;
8438657caa6SPyun YongHyeon /* Rx stats. */
8448657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO) |
8458657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI)) << 16;
8468657caa6SPyun YongHyeon val &= 0x000FFFFF;
8478657caa6SPyun YongHyeon stats->rx_bytes += val;
8488657caa6SPyun YongHyeon stats->rx_frames += CSR_READ_2(sc, STE_STAT_RX_FRAMES);
8498657caa6SPyun YongHyeon stats->rx_bcast_frames += CSR_READ_1(sc, STE_STAT_RX_BCAST);
8508657caa6SPyun YongHyeon stats->rx_mcast_frames += CSR_READ_1(sc, STE_STAT_RX_MCAST);
8518657caa6SPyun YongHyeon stats->rx_lost_frames += CSR_READ_1(sc, STE_STAT_RX_LOST);
8528657caa6SPyun YongHyeon /* Tx stats. */
8538657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO) |
8548657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI)) << 16;
8558657caa6SPyun YongHyeon val &= 0x000FFFFF;
8568657caa6SPyun YongHyeon stats->tx_bytes += val;
8578657caa6SPyun YongHyeon stats->tx_frames += CSR_READ_2(sc, STE_STAT_TX_FRAMES);
8588657caa6SPyun YongHyeon stats->tx_bcast_frames += CSR_READ_1(sc, STE_STAT_TX_BCAST);
8598657caa6SPyun YongHyeon stats->tx_mcast_frames += CSR_READ_1(sc, STE_STAT_TX_MCAST);
8608657caa6SPyun YongHyeon stats->tx_carrsense_errs += CSR_READ_1(sc, STE_STAT_CARRIER_ERR);
8618657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_SINGLE_COLLS);
8628657caa6SPyun YongHyeon stats->tx_single_colls += val;
863c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val);
8648657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_MULTI_COLLS);
8658657caa6SPyun YongHyeon stats->tx_multi_colls += val;
866c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val);
8678657caa6SPyun YongHyeon val += CSR_READ_1(sc, STE_STAT_LATE_COLLS);
8688657caa6SPyun YongHyeon stats->tx_late_colls += val;
869c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val);
8708657caa6SPyun YongHyeon stats->tx_frames_defered += CSR_READ_1(sc, STE_STAT_TX_DEFER);
8718657caa6SPyun YongHyeon stats->tx_excess_defers += CSR_READ_1(sc, STE_STAT_TX_EXDEFER);
8728657caa6SPyun YongHyeon stats->tx_abort += CSR_READ_1(sc, STE_STAT_TX_ABORT);
873c8befdd5SWarner Losh }
874c8befdd5SWarner Losh
875c8befdd5SWarner Losh /*
876c8befdd5SWarner Losh * Probe for a Sundance ST201 chip. Check the PCI vendor and device
877c8befdd5SWarner Losh * IDs against our list and return a device name if we find a match.
878c8befdd5SWarner Losh */
879c8befdd5SWarner Losh static int
ste_probe(device_t dev)88060270842SPyun YongHyeon ste_probe(device_t dev)
881c8befdd5SWarner Losh {
8828c1093fcSMarius Strobl const struct ste_type *t;
883c8befdd5SWarner Losh
884c8befdd5SWarner Losh t = ste_devs;
885c8befdd5SWarner Losh
886c8befdd5SWarner Losh while (t->ste_name != NULL) {
887c8befdd5SWarner Losh if ((pci_get_vendor(dev) == t->ste_vid) &&
888c8befdd5SWarner Losh (pci_get_device(dev) == t->ste_did)) {
889c8befdd5SWarner Losh device_set_desc(dev, t->ste_name);
890c8befdd5SWarner Losh return (BUS_PROBE_DEFAULT);
891c8befdd5SWarner Losh }
892c8befdd5SWarner Losh t++;
893c8befdd5SWarner Losh }
894c8befdd5SWarner Losh
895c8befdd5SWarner Losh return (ENXIO);
896c8befdd5SWarner Losh }
897c8befdd5SWarner Losh
898c8befdd5SWarner Losh /*
899c8befdd5SWarner Losh * Attach the interface. Allocate softc structures, do ifmedia
900c8befdd5SWarner Losh * setup and ethernet/BPF attach.
901c8befdd5SWarner Losh */
902c8befdd5SWarner Losh static int
ste_attach(device_t dev)90360270842SPyun YongHyeon ste_attach(device_t dev)
904c8befdd5SWarner Losh {
905c8befdd5SWarner Losh struct ste_softc *sc;
9066712df3aSJustin Hibbits if_t ifp;
907fcd8385eSPyun YongHyeon uint16_t eaddr[ETHER_ADDR_LEN / 2];
908*ddaf6524SJohn Baldwin int error = 0, phy, prefer_iomap, rid;
909c8befdd5SWarner Losh
910c8befdd5SWarner Losh sc = device_get_softc(dev);
911c8befdd5SWarner Losh sc->ste_dev = dev;
912c8befdd5SWarner Losh
913c8befdd5SWarner Losh /*
914c8befdd5SWarner Losh * Only use one PHY since this chip reports multiple
915c8befdd5SWarner Losh * Note on the DFE-550 the PHY is at 1 on the DFE-580
916c8befdd5SWarner Losh * it is at 0 & 1. It is rev 0x12.
917c8befdd5SWarner Losh */
918c8befdd5SWarner Losh if (pci_get_vendor(dev) == DL_VENDORID &&
919c8befdd5SWarner Losh pci_get_device(dev) == DL_DEVICEID_DL10050 &&
920c8befdd5SWarner Losh pci_get_revid(dev) == 0x12 )
9214465097bSPyun YongHyeon sc->ste_flags |= STE_FLAG_ONE_PHY;
922c8befdd5SWarner Losh
923c8befdd5SWarner Losh mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
924c8befdd5SWarner Losh MTX_DEF);
925c8befdd5SWarner Losh /*
926c8befdd5SWarner Losh * Map control/status registers.
927c8befdd5SWarner Losh */
928c8befdd5SWarner Losh pci_enable_busmaster(dev);
929c8befdd5SWarner Losh
930497ffa52SPyun YongHyeon /*
931497ffa52SPyun YongHyeon * Prefer memory space register mapping over IO space but use
932497ffa52SPyun YongHyeon * IO space for a device that is known to have issues on memory
933497ffa52SPyun YongHyeon * mapping.
934497ffa52SPyun YongHyeon */
935497ffa52SPyun YongHyeon prefer_iomap = 0;
936497ffa52SPyun YongHyeon if (pci_get_device(dev) == ST_DEVICEID_ST201_1)
937497ffa52SPyun YongHyeon prefer_iomap = 1;
938497ffa52SPyun YongHyeon else
939497ffa52SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev),
940497ffa52SPyun YongHyeon device_get_unit(sc->ste_dev), "prefer_iomap",
941497ffa52SPyun YongHyeon &prefer_iomap);
942497ffa52SPyun YongHyeon if (prefer_iomap == 0) {
943c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(1);
944c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_MEMORY;
945c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type,
946c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE);
947497ffa52SPyun YongHyeon }
948497ffa52SPyun YongHyeon if (prefer_iomap || sc->ste_res == NULL) {
949c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(0);
950c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_IOPORT;
951c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type,
952c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE);
953c0270e60SPyun YongHyeon }
954c8befdd5SWarner Losh if (sc->ste_res == NULL) {
955c8befdd5SWarner Losh device_printf(dev, "couldn't map ports/memory\n");
956c8befdd5SWarner Losh error = ENXIO;
957c8befdd5SWarner Losh goto fail;
958c8befdd5SWarner Losh }
959c8befdd5SWarner Losh
960c8befdd5SWarner Losh /* Allocate interrupt */
961c8befdd5SWarner Losh rid = 0;
962c8befdd5SWarner Losh sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
963c8befdd5SWarner Losh RF_SHAREABLE | RF_ACTIVE);
964c8befdd5SWarner Losh
965c8befdd5SWarner Losh if (sc->ste_irq == NULL) {
966c8befdd5SWarner Losh device_printf(dev, "couldn't map interrupt\n");
967c8befdd5SWarner Losh error = ENXIO;
968c8befdd5SWarner Losh goto fail;
969c8befdd5SWarner Losh }
970c8befdd5SWarner Losh
97110f695eeSPyun YongHyeon callout_init_mtx(&sc->ste_callout, &sc->ste_mtx, 0);
972c8befdd5SWarner Losh
973c8befdd5SWarner Losh /* Reset the adapter. */
974c8befdd5SWarner Losh ste_reset(sc);
975c8befdd5SWarner Losh
976c8befdd5SWarner Losh /*
977c8befdd5SWarner Losh * Get station address from the EEPROM.
978c8befdd5SWarner Losh */
979fcd8385eSPyun YongHyeon if (ste_read_eeprom(sc, eaddr, STE_EEADDR_NODE0, ETHER_ADDR_LEN / 2)) {
980c8befdd5SWarner Losh device_printf(dev, "failed to read station address\n");
981c2ede4b3SMartin Blapp error = ENXIO;
982c8befdd5SWarner Losh goto fail;
983c8befdd5SWarner Losh }
9848657caa6SPyun YongHyeon ste_sysctl_node(sc);
985c8befdd5SWarner Losh
986a1b2c209SPyun YongHyeon if ((error = ste_dma_alloc(sc)) != 0)
987c8befdd5SWarner Losh goto fail;
988c8befdd5SWarner Losh
989c8befdd5SWarner Losh ifp = sc->ste_ifp = if_alloc(IFT_ETHER);
990c8befdd5SWarner Losh
991c8befdd5SWarner Losh /* Do MII setup. */
9928e5d93dbSMarius Strobl phy = MII_PHY_ANY;
9938e5d93dbSMarius Strobl if ((sc->ste_flags & STE_FLAG_ONE_PHY) != 0)
9948e5d93dbSMarius Strobl phy = 0;
9958e5d93dbSMarius Strobl error = mii_attach(dev, &sc->ste_miibus, ifp, ste_ifmedia_upd,
9968e5d93dbSMarius Strobl ste_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
9978e5d93dbSMarius Strobl if (error != 0) {
9988e5d93dbSMarius Strobl device_printf(dev, "attaching PHYs failed\n");
999c8befdd5SWarner Losh goto fail;
1000c8befdd5SWarner Losh }
1001c8befdd5SWarner Losh
10026712df3aSJustin Hibbits if_setsoftc(ifp, sc);
1003c8befdd5SWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev));
10046712df3aSJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
10056712df3aSJustin Hibbits if_setioctlfn(ifp, ste_ioctl);
10066712df3aSJustin Hibbits if_setstartfn(ifp, ste_start);
10076712df3aSJustin Hibbits if_setinitfn(ifp, ste_init);
10086712df3aSJustin Hibbits if_setsendqlen(ifp, STE_TX_LIST_CNT - 1);
10096712df3aSJustin Hibbits if_setsendqready(ifp);
1010c8befdd5SWarner Losh
1011c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH;
1012c8befdd5SWarner Losh
1013c8befdd5SWarner Losh /*
1014c8befdd5SWarner Losh * Call MI attach routine.
1015c8befdd5SWarner Losh */
1016fcd8385eSPyun YongHyeon ether_ifattach(ifp, (uint8_t *)eaddr);
1017c8befdd5SWarner Losh
1018c8befdd5SWarner Losh /*
1019c8befdd5SWarner Losh * Tell the upper layer(s) we support long frames.
1020c8befdd5SWarner Losh */
10216712df3aSJustin Hibbits if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
10226712df3aSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0);
1023*ddaf6524SJohn Baldwin if (pci_has_pm(dev))
10246712df3aSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_WOL_MAGIC, 0);
10256712df3aSJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
1026c8befdd5SWarner Losh #ifdef DEVICE_POLLING
10276712df3aSJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
1028c8befdd5SWarner Losh #endif
1029c8befdd5SWarner Losh
1030c8befdd5SWarner Losh /* Hook interrupt last to avoid having to lock softc */
1031c8befdd5SWarner Losh error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET | INTR_MPSAFE,
1032c8befdd5SWarner Losh NULL, ste_intr, sc, &sc->ste_intrhand);
1033c8befdd5SWarner Losh
1034c8befdd5SWarner Losh if (error) {
1035c8befdd5SWarner Losh device_printf(dev, "couldn't set up irq\n");
1036c8befdd5SWarner Losh ether_ifdetach(ifp);
1037c8befdd5SWarner Losh goto fail;
1038c8befdd5SWarner Losh }
1039c8befdd5SWarner Losh
1040c8befdd5SWarner Losh fail:
1041c8befdd5SWarner Losh if (error)
1042c8befdd5SWarner Losh ste_detach(dev);
1043c8befdd5SWarner Losh
1044c8befdd5SWarner Losh return (error);
1045c8befdd5SWarner Losh }
1046c8befdd5SWarner Losh
1047c8befdd5SWarner Losh /*
1048c8befdd5SWarner Losh * Shutdown hardware and free up resources. This can be called any
1049c8befdd5SWarner Losh * time after the mutex has been initialized. It is called in both
1050c8befdd5SWarner Losh * the error case in attach and the normal detach case so it needs
1051c8befdd5SWarner Losh * to be careful about only freeing resources that have actually been
1052c8befdd5SWarner Losh * allocated.
1053c8befdd5SWarner Losh */
1054c8befdd5SWarner Losh static int
ste_detach(device_t dev)105560270842SPyun YongHyeon ste_detach(device_t dev)
1056c8befdd5SWarner Losh {
1057c8befdd5SWarner Losh struct ste_softc *sc;
10586712df3aSJustin Hibbits if_t ifp;
1059c8befdd5SWarner Losh
1060c8befdd5SWarner Losh sc = device_get_softc(dev);
1061c8befdd5SWarner Losh KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized"));
1062c8befdd5SWarner Losh ifp = sc->ste_ifp;
1063c8befdd5SWarner Losh
1064c8befdd5SWarner Losh #ifdef DEVICE_POLLING
10656712df3aSJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING)
1066c8befdd5SWarner Losh ether_poll_deregister(ifp);
1067c8befdd5SWarner Losh #endif
1068c8befdd5SWarner Losh
1069c8befdd5SWarner Losh /* These should only be active if attach succeeded */
1070c8befdd5SWarner Losh if (device_is_attached(dev)) {
10717cf545d0SJohn Baldwin ether_ifdetach(ifp);
1072c8befdd5SWarner Losh STE_LOCK(sc);
1073c8befdd5SWarner Losh ste_stop(sc);
1074c8befdd5SWarner Losh STE_UNLOCK(sc);
107510f695eeSPyun YongHyeon callout_drain(&sc->ste_callout);
1076c8befdd5SWarner Losh }
1077c8befdd5SWarner Losh bus_generic_detach(dev);
1078c8befdd5SWarner Losh
1079c8befdd5SWarner Losh if (sc->ste_intrhand)
1080c8befdd5SWarner Losh bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand);
1081c8befdd5SWarner Losh if (sc->ste_irq)
1082c8befdd5SWarner Losh bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq);
1083c8befdd5SWarner Losh if (sc->ste_res)
1084c0270e60SPyun YongHyeon bus_release_resource(dev, sc->ste_res_type, sc->ste_res_id,
1085c0270e60SPyun YongHyeon sc->ste_res);
1086c8befdd5SWarner Losh
1087c8befdd5SWarner Losh if (ifp)
1088c8befdd5SWarner Losh if_free(ifp);
1089c8befdd5SWarner Losh
1090a1b2c209SPyun YongHyeon ste_dma_free(sc);
1091c8befdd5SWarner Losh mtx_destroy(&sc->ste_mtx);
1092c8befdd5SWarner Losh
1093c8befdd5SWarner Losh return (0);
1094c8befdd5SWarner Losh }
1095c8befdd5SWarner Losh
1096a1b2c209SPyun YongHyeon struct ste_dmamap_arg {
1097a1b2c209SPyun YongHyeon bus_addr_t ste_busaddr;
1098a1b2c209SPyun YongHyeon };
1099a1b2c209SPyun YongHyeon
1100a1b2c209SPyun YongHyeon static void
ste_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)1101a1b2c209SPyun YongHyeon ste_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1102c8befdd5SWarner Losh {
1103a1b2c209SPyun YongHyeon struct ste_dmamap_arg *ctx;
1104c8befdd5SWarner Losh
1105a1b2c209SPyun YongHyeon if (error != 0)
1106a1b2c209SPyun YongHyeon return;
1107a1b2c209SPyun YongHyeon
1108a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1109a1b2c209SPyun YongHyeon
1110a1b2c209SPyun YongHyeon ctx = (struct ste_dmamap_arg *)arg;
1111a1b2c209SPyun YongHyeon ctx->ste_busaddr = segs[0].ds_addr;
1112c8befdd5SWarner Losh }
1113c8befdd5SWarner Losh
1114a1b2c209SPyun YongHyeon static int
ste_dma_alloc(struct ste_softc * sc)1115a1b2c209SPyun YongHyeon ste_dma_alloc(struct ste_softc *sc)
1116a1b2c209SPyun YongHyeon {
1117a1b2c209SPyun YongHyeon struct ste_chain *txc;
1118a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc;
1119a1b2c209SPyun YongHyeon struct ste_dmamap_arg ctx;
1120a1b2c209SPyun YongHyeon int error, i;
1121c8befdd5SWarner Losh
1122a1b2c209SPyun YongHyeon /* Create parent DMA tag. */
1123a1b2c209SPyun YongHyeon error = bus_dma_tag_create(
1124a1b2c209SPyun YongHyeon bus_get_dma_tag(sc->ste_dev), /* parent */
1125a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */
1126a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
1127a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */
1128a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */
1129a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */
1130a1b2c209SPyun YongHyeon 0, /* nsegments */
1131a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
1132a1b2c209SPyun YongHyeon 0, /* flags */
1133a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */
1134a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_parent_tag);
1135a1b2c209SPyun YongHyeon if (error != 0) {
1136a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1137a1b2c209SPyun YongHyeon "could not create parent DMA tag.\n");
1138a1b2c209SPyun YongHyeon goto fail;
1139a1b2c209SPyun YongHyeon }
1140c8befdd5SWarner Losh
1141a1b2c209SPyun YongHyeon /* Create DMA tag for Tx descriptor list. */
1142a1b2c209SPyun YongHyeon error = bus_dma_tag_create(
1143a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */
1144a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */
1145a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */
1146a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */
1147a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */
1148a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsize */
1149a1b2c209SPyun YongHyeon 1, /* nsegments */
1150a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsegsize */
1151a1b2c209SPyun YongHyeon 0, /* flags */
1152a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */
1153a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_tag);
1154a1b2c209SPyun YongHyeon if (error != 0) {
1155a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1156a1b2c209SPyun YongHyeon "could not create Tx list DMA tag.\n");
1157a1b2c209SPyun YongHyeon goto fail;
1158a1b2c209SPyun YongHyeon }
1159a1b2c209SPyun YongHyeon
1160a1b2c209SPyun YongHyeon /* Create DMA tag for Rx descriptor list. */
1161a1b2c209SPyun YongHyeon error = bus_dma_tag_create(
1162a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */
1163a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */
1164a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */
1165a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */
1166a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */
1167a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsize */
1168a1b2c209SPyun YongHyeon 1, /* nsegments */
1169a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsegsize */
1170a1b2c209SPyun YongHyeon 0, /* flags */
1171a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */
1172a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_tag);
1173a1b2c209SPyun YongHyeon if (error != 0) {
1174a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1175a1b2c209SPyun YongHyeon "could not create Rx list DMA tag.\n");
1176a1b2c209SPyun YongHyeon goto fail;
1177a1b2c209SPyun YongHyeon }
1178a1b2c209SPyun YongHyeon
1179a1b2c209SPyun YongHyeon /* Create DMA tag for Tx buffers. */
1180a1b2c209SPyun YongHyeon error = bus_dma_tag_create(
1181a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */
1182a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */
1183a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */
1184a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */
1185a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */
1186a1b2c209SPyun YongHyeon MCLBYTES * STE_MAXFRAGS, /* maxsize */
1187a1b2c209SPyun YongHyeon STE_MAXFRAGS, /* nsegments */
1188a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */
1189a1b2c209SPyun YongHyeon 0, /* flags */
1190a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */
1191a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_tag);
1192a1b2c209SPyun YongHyeon if (error != 0) {
1193a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Tx DMA tag.\n");
1194a1b2c209SPyun YongHyeon goto fail;
1195a1b2c209SPyun YongHyeon }
1196a1b2c209SPyun YongHyeon
1197a1b2c209SPyun YongHyeon /* Create DMA tag for Rx buffers. */
1198a1b2c209SPyun YongHyeon error = bus_dma_tag_create(
1199a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */
1200a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */
1201a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */
1202a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */
1203a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */
1204a1b2c209SPyun YongHyeon MCLBYTES, /* maxsize */
1205a1b2c209SPyun YongHyeon 1, /* nsegments */
1206a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */
1207a1b2c209SPyun YongHyeon 0, /* flags */
1208a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */
1209a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_tag);
1210a1b2c209SPyun YongHyeon if (error != 0) {
1211a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Rx DMA tag.\n");
1212a1b2c209SPyun YongHyeon goto fail;
1213a1b2c209SPyun YongHyeon }
1214a1b2c209SPyun YongHyeon
1215a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Tx list. */
1216a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_tx_list_tag,
1217a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_tx_list,
1218a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1219a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_map);
1220a1b2c209SPyun YongHyeon if (error != 0) {
1221a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1222a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Tx list.\n");
1223a1b2c209SPyun YongHyeon goto fail;
1224a1b2c209SPyun YongHyeon }
1225a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0;
1226a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_tx_list_tag,
1227a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, sc->ste_ldata.ste_tx_list,
1228a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, ste_dmamap_cb, &ctx, 0);
1229a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) {
1230a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1231a1b2c209SPyun YongHyeon "could not load DMA'able memory for Tx list.\n");
1232a1b2c209SPyun YongHyeon goto fail;
1233a1b2c209SPyun YongHyeon }
1234a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list_paddr = ctx.ste_busaddr;
1235a1b2c209SPyun YongHyeon
1236a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Rx list. */
1237a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_rx_list_tag,
1238a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_rx_list,
1239a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1240a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_map);
1241a1b2c209SPyun YongHyeon if (error != 0) {
1242a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1243a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Rx list.\n");
1244a1b2c209SPyun YongHyeon goto fail;
1245a1b2c209SPyun YongHyeon }
1246a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0;
1247a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_rx_list_tag,
1248a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, sc->ste_ldata.ste_rx_list,
1249a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, ste_dmamap_cb, &ctx, 0);
1250a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) {
1251a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1252a1b2c209SPyun YongHyeon "could not load DMA'able memory for Rx list.\n");
1253a1b2c209SPyun YongHyeon goto fail;
1254a1b2c209SPyun YongHyeon }
1255a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list_paddr = ctx.ste_busaddr;
1256a1b2c209SPyun YongHyeon
1257a1b2c209SPyun YongHyeon /* Create DMA maps for Tx buffers. */
1258a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) {
1259a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i];
1260a1b2c209SPyun YongHyeon txc->ste_ptr = NULL;
1261a1b2c209SPyun YongHyeon txc->ste_mbuf = NULL;
1262a1b2c209SPyun YongHyeon txc->ste_next = NULL;
1263a1b2c209SPyun YongHyeon txc->ste_phys = 0;
1264a1b2c209SPyun YongHyeon txc->ste_map = NULL;
1265a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_tx_tag, 0,
1266a1b2c209SPyun YongHyeon &txc->ste_map);
1267a1b2c209SPyun YongHyeon if (error != 0) {
1268a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1269a1b2c209SPyun YongHyeon "could not create Tx dmamap.\n");
1270a1b2c209SPyun YongHyeon goto fail;
1271a1b2c209SPyun YongHyeon }
1272a1b2c209SPyun YongHyeon }
1273a1b2c209SPyun YongHyeon /* Create DMA maps for Rx buffers. */
1274a1b2c209SPyun YongHyeon if ((error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0,
1275a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_sparemap)) != 0) {
1276a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1277a1b2c209SPyun YongHyeon "could not create spare Rx dmamap.\n");
1278a1b2c209SPyun YongHyeon goto fail;
1279a1b2c209SPyun YongHyeon }
1280a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) {
1281a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i];
1282a1b2c209SPyun YongHyeon rxc->ste_ptr = NULL;
1283a1b2c209SPyun YongHyeon rxc->ste_mbuf = NULL;
1284a1b2c209SPyun YongHyeon rxc->ste_next = NULL;
1285a1b2c209SPyun YongHyeon rxc->ste_map = NULL;
1286a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0,
1287a1b2c209SPyun YongHyeon &rxc->ste_map);
1288a1b2c209SPyun YongHyeon if (error != 0) {
1289a1b2c209SPyun YongHyeon device_printf(sc->ste_dev,
1290a1b2c209SPyun YongHyeon "could not create Rx dmamap.\n");
1291a1b2c209SPyun YongHyeon goto fail;
1292a1b2c209SPyun YongHyeon }
1293a1b2c209SPyun YongHyeon }
1294a1b2c209SPyun YongHyeon
1295a1b2c209SPyun YongHyeon fail:
1296a1b2c209SPyun YongHyeon return (error);
1297a1b2c209SPyun YongHyeon }
1298a1b2c209SPyun YongHyeon
1299a1b2c209SPyun YongHyeon static void
ste_dma_free(struct ste_softc * sc)1300a1b2c209SPyun YongHyeon ste_dma_free(struct ste_softc *sc)
1301a1b2c209SPyun YongHyeon {
1302a1b2c209SPyun YongHyeon struct ste_chain *txc;
1303a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc;
1304a1b2c209SPyun YongHyeon int i;
1305a1b2c209SPyun YongHyeon
1306a1b2c209SPyun YongHyeon /* Tx buffers. */
1307a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_tag != NULL) {
1308a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) {
1309a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i];
1310a1b2c209SPyun YongHyeon if (txc->ste_map != NULL) {
1311a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_tx_tag,
1312a1b2c209SPyun YongHyeon txc->ste_map);
1313a1b2c209SPyun YongHyeon txc->ste_map = NULL;
1314a1b2c209SPyun YongHyeon }
1315a1b2c209SPyun YongHyeon }
1316a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_tag);
1317a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_tag = NULL;
1318a1b2c209SPyun YongHyeon }
1319a1b2c209SPyun YongHyeon /* Rx buffers. */
1320a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_tag != NULL) {
1321a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) {
1322a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i];
1323a1b2c209SPyun YongHyeon if (rxc->ste_map != NULL) {
1324a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag,
1325a1b2c209SPyun YongHyeon rxc->ste_map);
1326a1b2c209SPyun YongHyeon rxc->ste_map = NULL;
1327a1b2c209SPyun YongHyeon }
1328a1b2c209SPyun YongHyeon }
1329a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_sparemap != NULL) {
1330a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag,
1331a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap);
1332a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = NULL;
1333a1b2c209SPyun YongHyeon }
1334a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_tag);
1335a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_tag = NULL;
1336a1b2c209SPyun YongHyeon }
1337a1b2c209SPyun YongHyeon /* Tx descriptor list. */
1338a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_tag != NULL) {
1339068d8643SJohn Baldwin if (sc->ste_ldata.ste_tx_list_paddr != 0)
1340a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_list_tag,
1341a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map);
1342068d8643SJohn Baldwin if (sc->ste_ldata.ste_tx_list != NULL)
1343a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_tx_list_tag,
1344a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list,
1345a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map);
1346a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list = NULL;
1347068d8643SJohn Baldwin sc->ste_ldata.ste_tx_list_paddr = 0;
1348a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_list_tag);
1349a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_tag = NULL;
1350a1b2c209SPyun YongHyeon }
1351a1b2c209SPyun YongHyeon /* Rx descriptor list. */
1352a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_tag != NULL) {
1353068d8643SJohn Baldwin if (sc->ste_ldata.ste_rx_list_paddr != 0)
1354a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_list_tag,
1355a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map);
1356068d8643SJohn Baldwin if (sc->ste_ldata.ste_rx_list != NULL)
1357a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_rx_list_tag,
1358a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list,
1359a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map);
1360a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list = NULL;
1361068d8643SJohn Baldwin sc->ste_ldata.ste_rx_list_paddr = 0;
1362a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_list_tag);
1363a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_tag = NULL;
1364a1b2c209SPyun YongHyeon }
1365a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_parent_tag != NULL) {
1366a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_parent_tag);
1367a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag = NULL;
1368a1b2c209SPyun YongHyeon }
1369a1b2c209SPyun YongHyeon }
1370a1b2c209SPyun YongHyeon
1371a1b2c209SPyun YongHyeon static int
ste_newbuf(struct ste_softc * sc,struct ste_chain_onefrag * rxc)1372a1b2c209SPyun YongHyeon ste_newbuf(struct ste_softc *sc, struct ste_chain_onefrag *rxc)
1373a1b2c209SPyun YongHyeon {
1374a1b2c209SPyun YongHyeon struct mbuf *m;
1375a1b2c209SPyun YongHyeon bus_dma_segment_t segs[1];
1376a1b2c209SPyun YongHyeon bus_dmamap_t map;
1377a1b2c209SPyun YongHyeon int error, nsegs;
1378a1b2c209SPyun YongHyeon
1379c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1380a1b2c209SPyun YongHyeon if (m == NULL)
1381a1b2c209SPyun YongHyeon return (ENOBUFS);
1382a1b2c209SPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES;
1383a1b2c209SPyun YongHyeon m_adj(m, ETHER_ALIGN);
1384a1b2c209SPyun YongHyeon
1385a1b2c209SPyun YongHyeon if ((error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_rx_tag,
1386a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap, m, segs, &nsegs, 0)) != 0) {
1387a1b2c209SPyun YongHyeon m_freem(m);
1388a1b2c209SPyun YongHyeon return (error);
1389a1b2c209SPyun YongHyeon }
1390a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1391a1b2c209SPyun YongHyeon
1392a1b2c209SPyun YongHyeon if (rxc->ste_mbuf != NULL) {
1393a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map,
1394a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD);
1395a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, rxc->ste_map);
1396a1b2c209SPyun YongHyeon }
1397a1b2c209SPyun YongHyeon map = rxc->ste_map;
1398a1b2c209SPyun YongHyeon rxc->ste_map = sc->ste_cdata.ste_rx_sparemap;
1399a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = map;
1400a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map,
1401a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD);
1402a1b2c209SPyun YongHyeon rxc->ste_mbuf = m;
1403a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_status = 0;
1404a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_addr = htole32(segs[0].ds_addr);
1405a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_len = htole32(segs[0].ds_len |
1406a1b2c209SPyun YongHyeon STE_FRAG_LAST);
1407c8befdd5SWarner Losh return (0);
1408c8befdd5SWarner Losh }
1409c8befdd5SWarner Losh
1410c8befdd5SWarner Losh static int
ste_init_rx_list(struct ste_softc * sc)141160270842SPyun YongHyeon ste_init_rx_list(struct ste_softc *sc)
1412c8befdd5SWarner Losh {
1413c8befdd5SWarner Losh struct ste_chain_data *cd;
1414c8befdd5SWarner Losh struct ste_list_data *ld;
1415a1b2c209SPyun YongHyeon int error, i;
1416c8befdd5SWarner Losh
1417fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0;
1418c8befdd5SWarner Losh cd = &sc->ste_cdata;
1419a1b2c209SPyun YongHyeon ld = &sc->ste_ldata;
1420a1b2c209SPyun YongHyeon bzero(ld->ste_rx_list, STE_RX_LIST_SZ);
1421c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) {
1422c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i];
1423a1b2c209SPyun YongHyeon error = ste_newbuf(sc, &cd->ste_rx_chain[i]);
1424a1b2c209SPyun YongHyeon if (error != 0)
1425a1b2c209SPyun YongHyeon return (error);
1426c8befdd5SWarner Losh if (i == (STE_RX_LIST_CNT - 1)) {
1427a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[0];
1428e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next =
1429e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr +
1430e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * 0));
1431c8befdd5SWarner Losh } else {
1432a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[i + 1];
1433e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next =
1434e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr +
1435e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * (i + 1)));
1436c8befdd5SWarner Losh }
1437c8befdd5SWarner Losh }
1438c8befdd5SWarner Losh
1439c8befdd5SWarner Losh cd->ste_rx_head = &cd->ste_rx_chain[0];
1440a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag,
1441a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map,
1442a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1443c8befdd5SWarner Losh
1444c8befdd5SWarner Losh return (0);
1445c8befdd5SWarner Losh }
1446c8befdd5SWarner Losh
1447c8befdd5SWarner Losh static void
ste_init_tx_list(struct ste_softc * sc)144860270842SPyun YongHyeon ste_init_tx_list(struct ste_softc *sc)
1449c8befdd5SWarner Losh {
1450c8befdd5SWarner Losh struct ste_chain_data *cd;
1451c8befdd5SWarner Losh struct ste_list_data *ld;
1452c8befdd5SWarner Losh int i;
1453c8befdd5SWarner Losh
1454c8befdd5SWarner Losh cd = &sc->ste_cdata;
1455a1b2c209SPyun YongHyeon ld = &sc->ste_ldata;
1456a1b2c209SPyun YongHyeon bzero(ld->ste_tx_list, STE_TX_LIST_SZ);
1457c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) {
1458c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i];
1459a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_mbuf = NULL;
1460a1b2c209SPyun YongHyeon if (i == (STE_TX_LIST_CNT - 1)) {
1461a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[0];
1462a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO(
1463a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr +
1464a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * 0)));
1465a1b2c209SPyun YongHyeon } else {
1466a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1];
1467a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO(
1468a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr +
1469a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * (i + 1))));
1470a1b2c209SPyun YongHyeon }
1471c8befdd5SWarner Losh }
1472c8befdd5SWarner Losh
1473a1b2c209SPyun YongHyeon cd->ste_last_tx = NULL;
1474c8befdd5SWarner Losh cd->ste_tx_prod = 0;
1475c8befdd5SWarner Losh cd->ste_tx_cons = 0;
1476a1b2c209SPyun YongHyeon cd->ste_tx_cnt = 0;
1477a1b2c209SPyun YongHyeon
1478a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag,
1479a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map,
1480a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1481c8befdd5SWarner Losh }
1482c8befdd5SWarner Losh
1483c8befdd5SWarner Losh static void
ste_init(void * xsc)148460270842SPyun YongHyeon ste_init(void *xsc)
1485c8befdd5SWarner Losh {
1486c8befdd5SWarner Losh struct ste_softc *sc;
1487c8befdd5SWarner Losh
1488c8befdd5SWarner Losh sc = xsc;
1489c8befdd5SWarner Losh STE_LOCK(sc);
1490c8befdd5SWarner Losh ste_init_locked(sc);
1491c8befdd5SWarner Losh STE_UNLOCK(sc);
1492c8befdd5SWarner Losh }
1493c8befdd5SWarner Losh
1494c8befdd5SWarner Losh static void
ste_init_locked(struct ste_softc * sc)149560270842SPyun YongHyeon ste_init_locked(struct ste_softc *sc)
1496c8befdd5SWarner Losh {
14976712df3aSJustin Hibbits if_t ifp;
1498bfe051bdSPyun YongHyeon struct mii_data *mii;
1499b4c170e1SPyun YongHyeon uint8_t val;
1500f2632c3bSPyun YongHyeon int i;
1501c8befdd5SWarner Losh
1502c8befdd5SWarner Losh STE_LOCK_ASSERT(sc);
1503c8befdd5SWarner Losh ifp = sc->ste_ifp;
1504bfe051bdSPyun YongHyeon mii = device_get_softc(sc->ste_miibus);
1505c8befdd5SWarner Losh
15066712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
150755d7003eSPyun YongHyeon return;
150855d7003eSPyun YongHyeon
1509c8befdd5SWarner Losh ste_stop(sc);
15108d9f6dd9SPyun YongHyeon /* Reset the chip to a known state. */
15118d9f6dd9SPyun YongHyeon ste_reset(sc);
1512c8befdd5SWarner Losh
1513c8befdd5SWarner Losh /* Init our MAC address */
1514c8befdd5SWarner Losh for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
1515c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PAR0 + i,
15166712df3aSJustin Hibbits ((if_getlladdr(sc->ste_ifp)[i] & 0xff) |
15176712df3aSJustin Hibbits if_getlladdr(sc->ste_ifp)[i + 1] << 8));
1518c8befdd5SWarner Losh }
1519c8befdd5SWarner Losh
1520c8befdd5SWarner Losh /* Init RX list */
1521a1b2c209SPyun YongHyeon if (ste_init_rx_list(sc) != 0) {
1522c8befdd5SWarner Losh device_printf(sc->ste_dev,
1523c8befdd5SWarner Losh "initialization failed: no memory for RX buffers\n");
1524c8befdd5SWarner Losh ste_stop(sc);
1525c8befdd5SWarner Losh return;
1526c8befdd5SWarner Losh }
1527c8befdd5SWarner Losh
1528c8befdd5SWarner Losh /* Set RX polling interval */
1529c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64);
1530c8befdd5SWarner Losh
1531c8befdd5SWarner Losh /* Init TX descriptors */
1532c8befdd5SWarner Losh ste_init_tx_list(sc);
1533c8befdd5SWarner Losh
1534b4c170e1SPyun YongHyeon /* Clear and disable WOL. */
1535b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT);
1536b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB |
1537b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB);
1538b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val);
1539b4c170e1SPyun YongHyeon
1540c8befdd5SWarner Losh /* Set the TX freethresh value */
1541c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8);
1542c8befdd5SWarner Losh
1543c8befdd5SWarner Losh /* Set the TX start threshold for best performance. */
1544c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh);
1545c8befdd5SWarner Losh
1546c8befdd5SWarner Losh /* Set the TX reclaim threshold. */
1547c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4));
1548c8befdd5SWarner Losh
1549931ec15aSPyun YongHyeon /* Accept VLAN length packets */
1550931ec15aSPyun YongHyeon CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN);
1551931ec15aSPyun YongHyeon
1552c8befdd5SWarner Losh /* Set up the RX filter. */
1553931ec15aSPyun YongHyeon ste_rxfilter(sc);
1554c8befdd5SWarner Losh
1555c8befdd5SWarner Losh /* Load the address of the RX list. */
1556c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL);
1557c8befdd5SWarner Losh ste_wait(sc);
1558c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_RX_DMALIST_PTR,
1559a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_rx_list_paddr));
1560c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL);
1561c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL);
1562c8befdd5SWarner Losh
1563a1b2c209SPyun YongHyeon /* Set TX polling interval(defer until we TX first packet). */
1564c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0);
1565c8befdd5SWarner Losh
1566c8befdd5SWarner Losh /* Load address of the TX list */
1567c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL);
1568c8befdd5SWarner Losh ste_wait(sc);
1569c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0);
1570c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL);
1571c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL);
1572c8befdd5SWarner Losh ste_wait(sc);
1573fabbaac5SPyun YongHyeon /* Select 3.2us timer. */
1574fabbaac5SPyun YongHyeon STE_CLRBIT4(sc, STE_DMACTL, STE_DMACTL_COUNTDOWN_SPEED |
1575fabbaac5SPyun YongHyeon STE_DMACTL_COUNTDOWN_MODE);
1576c8befdd5SWarner Losh
1577c8befdd5SWarner Losh /* Enable receiver and transmitter */
1578c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL0, 0);
1579c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL1, 0);
1580c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE);
1581c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE);
1582c8befdd5SWarner Losh
1583c8befdd5SWarner Losh /* Enable stats counters. */
1584c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE);
15858657caa6SPyun YongHyeon /* Clear stats counters. */
15868657caa6SPyun YongHyeon ste_stats_clear(sc);
1587c8befdd5SWarner Losh
1588fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0);
1589c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_ISR, 0xFFFF);
1590c8befdd5SWarner Losh #ifdef DEVICE_POLLING
1591c8befdd5SWarner Losh /* Disable interrupts if we are polling. */
15926712df3aSJustin Hibbits if (if_getcapenable(ifp) & IFCAP_POLLING)
1593c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0);
1594c8befdd5SWarner Losh else
1595c8befdd5SWarner Losh #endif
1596c8befdd5SWarner Losh /* Enable interrupts. */
1597c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS);
1598c8befdd5SWarner Losh
1599bfe051bdSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK;
1600bfe051bdSPyun YongHyeon /* Switch to the current media. */
1601bfe051bdSPyun YongHyeon mii_mediachg(mii);
1602c8befdd5SWarner Losh
16036712df3aSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
16046712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1605c8befdd5SWarner Losh
160610f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc);
1607c8befdd5SWarner Losh }
1608c8befdd5SWarner Losh
1609c8befdd5SWarner Losh static void
ste_stop(struct ste_softc * sc)161060270842SPyun YongHyeon ste_stop(struct ste_softc *sc)
1611c8befdd5SWarner Losh {
16126712df3aSJustin Hibbits if_t ifp;
1613a1b2c209SPyun YongHyeon struct ste_chain_onefrag *cur_rx;
1614a1b2c209SPyun YongHyeon struct ste_chain *cur_tx;
16158d9f6dd9SPyun YongHyeon uint32_t val;
1616f2632c3bSPyun YongHyeon int i;
1617c8befdd5SWarner Losh
1618c8befdd5SWarner Losh STE_LOCK_ASSERT(sc);
1619c8befdd5SWarner Losh ifp = sc->ste_ifp;
1620c8befdd5SWarner Losh
162110f695eeSPyun YongHyeon callout_stop(&sc->ste_callout);
162210f695eeSPyun YongHyeon sc->ste_timer = 0;
16236712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING|IFF_DRV_OACTIVE));
1624c8befdd5SWarner Losh
1625c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0);
1626fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0);
16278d9f6dd9SPyun YongHyeon /* Stop pending DMA. */
16288d9f6dd9SPyun YongHyeon val = CSR_READ_4(sc, STE_DMACTL);
16298d9f6dd9SPyun YongHyeon val |= STE_DMACTL_TXDMA_STALL | STE_DMACTL_RXDMA_STALL;
16308d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_DMACTL, val);
1631c8befdd5SWarner Losh ste_wait(sc);
16328d9f6dd9SPyun YongHyeon /* Disable auto-polling. */
16338d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 0);
16348d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0);
16358d9f6dd9SPyun YongHyeon /* Nullify DMA address to stop any further DMA. */
16368d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 0);
16378d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0);
16388d9f6dd9SPyun YongHyeon /* Stop TX/RX MAC. */
16398d9f6dd9SPyun YongHyeon val = CSR_READ_2(sc, STE_MACCTL1);
16408d9f6dd9SPyun YongHyeon val |= STE_MACCTL1_TX_DISABLE | STE_MACCTL1_RX_DISABLE |
16418d9f6dd9SPyun YongHyeon STE_MACCTL1_STATS_DISABLE;
16428d9f6dd9SPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, val);
16438d9f6dd9SPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) {
16448d9f6dd9SPyun YongHyeon DELAY(10);
16458d9f6dd9SPyun YongHyeon if ((CSR_READ_2(sc, STE_MACCTL1) & (STE_MACCTL1_TX_DISABLE |
16468d9f6dd9SPyun YongHyeon STE_MACCTL1_RX_DISABLE | STE_MACCTL1_STATS_DISABLE)) == 0)
16478d9f6dd9SPyun YongHyeon break;
16488d9f6dd9SPyun YongHyeon }
16498d9f6dd9SPyun YongHyeon if (i == STE_TIMEOUT)
16508d9f6dd9SPyun YongHyeon device_printf(sc->ste_dev, "Stopping MAC timed out\n");
16518d9f6dd9SPyun YongHyeon /* Acknowledge any pending interrupts. */
16528d9f6dd9SPyun YongHyeon CSR_READ_2(sc, STE_ISR_ACK);
16538d9f6dd9SPyun YongHyeon ste_stats_update(sc);
1654c8befdd5SWarner Losh
1655c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) {
1656a1b2c209SPyun YongHyeon cur_rx = &sc->ste_cdata.ste_rx_chain[i];
1657a1b2c209SPyun YongHyeon if (cur_rx->ste_mbuf != NULL) {
1658a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag,
1659a1b2c209SPyun YongHyeon cur_rx->ste_map, BUS_DMASYNC_POSTREAD);
1660a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag,
1661a1b2c209SPyun YongHyeon cur_rx->ste_map);
1662a1b2c209SPyun YongHyeon m_freem(cur_rx->ste_mbuf);
1663a1b2c209SPyun YongHyeon cur_rx->ste_mbuf = NULL;
1664c8befdd5SWarner Losh }
1665c8befdd5SWarner Losh }
1666c8befdd5SWarner Losh
1667c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) {
1668a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[i];
1669a1b2c209SPyun YongHyeon if (cur_tx->ste_mbuf != NULL) {
1670a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag,
1671a1b2c209SPyun YongHyeon cur_tx->ste_map, BUS_DMASYNC_POSTWRITE);
1672a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag,
1673a1b2c209SPyun YongHyeon cur_tx->ste_map);
1674a1b2c209SPyun YongHyeon m_freem(cur_tx->ste_mbuf);
1675a1b2c209SPyun YongHyeon cur_tx->ste_mbuf = NULL;
1676c8befdd5SWarner Losh }
1677c8befdd5SWarner Losh }
1678c8befdd5SWarner Losh }
1679c8befdd5SWarner Losh
1680c8befdd5SWarner Losh static void
ste_reset(struct ste_softc * sc)168160270842SPyun YongHyeon ste_reset(struct ste_softc *sc)
1682c8befdd5SWarner Losh {
168338c52cfdSPyun YongHyeon uint32_t ctl;
1684c8befdd5SWarner Losh int i;
1685c8befdd5SWarner Losh
168638c52cfdSPyun YongHyeon ctl = CSR_READ_4(sc, STE_ASICCTL);
168738c52cfdSPyun YongHyeon ctl |= STE_ASICCTL_GLOBAL_RESET | STE_ASICCTL_RX_RESET |
1688c8befdd5SWarner Losh STE_ASICCTL_TX_RESET | STE_ASICCTL_DMA_RESET |
1689c8befdd5SWarner Losh STE_ASICCTL_FIFO_RESET | STE_ASICCTL_NETWORK_RESET |
1690c8befdd5SWarner Losh STE_ASICCTL_AUTOINIT_RESET |STE_ASICCTL_HOST_RESET |
169138c52cfdSPyun YongHyeon STE_ASICCTL_EXTRESET_RESET;
169238c52cfdSPyun YongHyeon CSR_WRITE_4(sc, STE_ASICCTL, ctl);
169338c52cfdSPyun YongHyeon CSR_READ_4(sc, STE_ASICCTL);
169438c52cfdSPyun YongHyeon /*
169538c52cfdSPyun YongHyeon * Due to the need of accessing EEPROM controller can take
169638c52cfdSPyun YongHyeon * up to 1ms to complete the global reset.
169738c52cfdSPyun YongHyeon */
169838c52cfdSPyun YongHyeon DELAY(1000);
1699c8befdd5SWarner Losh
1700c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) {
1701c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY))
1702c8befdd5SWarner Losh break;
170338c52cfdSPyun YongHyeon DELAY(10);
1704c8befdd5SWarner Losh }
1705c8befdd5SWarner Losh
1706c8befdd5SWarner Losh if (i == STE_TIMEOUT)
1707c8befdd5SWarner Losh device_printf(sc->ste_dev, "global reset never completed\n");
1708c8befdd5SWarner Losh }
1709c8befdd5SWarner Losh
171081598b3eSPyun YongHyeon static void
ste_restart_tx(struct ste_softc * sc)171181598b3eSPyun YongHyeon ste_restart_tx(struct ste_softc *sc)
171281598b3eSPyun YongHyeon {
171381598b3eSPyun YongHyeon uint16_t mac;
171481598b3eSPyun YongHyeon int i;
171581598b3eSPyun YongHyeon
171681598b3eSPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) {
171781598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1);
171881598b3eSPyun YongHyeon mac |= STE_MACCTL1_TX_ENABLE;
171981598b3eSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, mac);
172081598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1);
172181598b3eSPyun YongHyeon if ((mac & STE_MACCTL1_TX_ENABLED) != 0)
172281598b3eSPyun YongHyeon break;
172381598b3eSPyun YongHyeon DELAY(10);
172481598b3eSPyun YongHyeon }
172581598b3eSPyun YongHyeon
172681598b3eSPyun YongHyeon if (i == STE_TIMEOUT)
172781598b3eSPyun YongHyeon device_printf(sc->ste_dev, "starting Tx failed");
172881598b3eSPyun YongHyeon }
172981598b3eSPyun YongHyeon
1730c8befdd5SWarner Losh static int
ste_ioctl(if_t ifp,u_long command,caddr_t data)17316712df3aSJustin Hibbits ste_ioctl(if_t ifp, u_long command, caddr_t data)
1732c8befdd5SWarner Losh {
1733c8befdd5SWarner Losh struct ste_softc *sc;
1734c8befdd5SWarner Losh struct ifreq *ifr;
1735c8befdd5SWarner Losh struct mii_data *mii;
1736b4c170e1SPyun YongHyeon int error = 0, mask;
1737c8befdd5SWarner Losh
17386712df3aSJustin Hibbits sc = if_getsoftc(ifp);
1739c8befdd5SWarner Losh ifr = (struct ifreq *)data;
1740c8befdd5SWarner Losh
1741c8befdd5SWarner Losh switch (command) {
1742c8befdd5SWarner Losh case SIOCSIFFLAGS:
1743c8befdd5SWarner Losh STE_LOCK(sc);
17446712df3aSJustin Hibbits if ((if_getflags(ifp) & IFF_UP) != 0) {
17456712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0 &&
17466712df3aSJustin Hibbits ((if_getflags(ifp) ^ sc->ste_if_flags) &
1747931ec15aSPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1748931ec15aSPyun YongHyeon ste_rxfilter(sc);
1749931ec15aSPyun YongHyeon else
1750c8befdd5SWarner Losh ste_init_locked(sc);
17516712df3aSJustin Hibbits } else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
1752c8befdd5SWarner Losh ste_stop(sc);
17536712df3aSJustin Hibbits sc->ste_if_flags = if_getflags(ifp);
1754c8befdd5SWarner Losh STE_UNLOCK(sc);
1755c8befdd5SWarner Losh break;
1756c8befdd5SWarner Losh case SIOCADDMULTI:
1757c8befdd5SWarner Losh case SIOCDELMULTI:
1758c8befdd5SWarner Losh STE_LOCK(sc);
17596712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
1760931ec15aSPyun YongHyeon ste_rxfilter(sc);
1761c8befdd5SWarner Losh STE_UNLOCK(sc);
1762c8befdd5SWarner Losh break;
1763c8befdd5SWarner Losh case SIOCGIFMEDIA:
1764c8befdd5SWarner Losh case SIOCSIFMEDIA:
1765c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus);
1766c8befdd5SWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
1767c8befdd5SWarner Losh break;
1768c8befdd5SWarner Losh case SIOCSIFCAP:
1769c8befdd5SWarner Losh STE_LOCK(sc);
17706712df3aSJustin Hibbits mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
1771b4c170e1SPyun YongHyeon #ifdef DEVICE_POLLING
1772b4c170e1SPyun YongHyeon if ((mask & IFCAP_POLLING) != 0 &&
17736712df3aSJustin Hibbits (IFCAP_POLLING & if_getcapabilities(ifp)) != 0) {
17746712df3aSJustin Hibbits if_togglecapenable(ifp, IFCAP_POLLING);
17756712df3aSJustin Hibbits if ((IFCAP_POLLING & if_getcapenable(ifp)) != 0) {
1776b4c170e1SPyun YongHyeon error = ether_poll_register(ste_poll, ifp);
1777b4c170e1SPyun YongHyeon if (error != 0) {
1778c8befdd5SWarner Losh STE_UNLOCK(sc);
1779b4c170e1SPyun YongHyeon break;
1780c8befdd5SWarner Losh }
1781b4c170e1SPyun YongHyeon /* Disable interrupts. */
1782b4c170e1SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, 0);
1783b4c170e1SPyun YongHyeon } else {
1784c8befdd5SWarner Losh error = ether_poll_deregister(ifp);
1785c8befdd5SWarner Losh /* Enable interrupts. */
1786c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS);
1787b4c170e1SPyun YongHyeon }
1788c8befdd5SWarner Losh }
1789c8befdd5SWarner Losh #endif /* DEVICE_POLLING */
1790b4c170e1SPyun YongHyeon if ((mask & IFCAP_WOL_MAGIC) != 0 &&
17916712df3aSJustin Hibbits (if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) != 0)
17926712df3aSJustin Hibbits if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
1793b4c170e1SPyun YongHyeon STE_UNLOCK(sc);
1794c8befdd5SWarner Losh break;
1795c8befdd5SWarner Losh default:
1796c8befdd5SWarner Losh error = ether_ioctl(ifp, command, data);
1797c8befdd5SWarner Losh break;
1798c8befdd5SWarner Losh }
1799c8befdd5SWarner Losh
1800c8befdd5SWarner Losh return (error);
1801c8befdd5SWarner Losh }
1802c8befdd5SWarner Losh
1803c8befdd5SWarner Losh static int
ste_encap(struct ste_softc * sc,struct mbuf ** m_head,struct ste_chain * txc)1804a1b2c209SPyun YongHyeon ste_encap(struct ste_softc *sc, struct mbuf **m_head, struct ste_chain *txc)
1805c8befdd5SWarner Losh {
1806a1b2c209SPyun YongHyeon struct ste_frag *frag;
1807c8befdd5SWarner Losh struct mbuf *m;
1808a1b2c209SPyun YongHyeon struct ste_desc *desc;
1809a1b2c209SPyun YongHyeon bus_dma_segment_t txsegs[STE_MAXFRAGS];
1810a1b2c209SPyun YongHyeon int error, i, nsegs;
1811c8befdd5SWarner Losh
1812a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc);
1813a1b2c209SPyun YongHyeon M_ASSERTPKTHDR((*m_head));
1814c8befdd5SWarner Losh
1815a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag,
1816a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0);
1817a1b2c209SPyun YongHyeon if (error == EFBIG) {
1818c6499eccSGleb Smirnoff m = m_collapse(*m_head, M_NOWAIT, STE_MAXFRAGS);
1819a1b2c209SPyun YongHyeon if (m == NULL) {
1820a1b2c209SPyun YongHyeon m_freem(*m_head);
1821a1b2c209SPyun YongHyeon *m_head = NULL;
1822a1b2c209SPyun YongHyeon return (ENOMEM);
1823c8befdd5SWarner Losh }
1824a1b2c209SPyun YongHyeon *m_head = m;
1825a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag,
1826a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0);
1827a1b2c209SPyun YongHyeon if (error != 0) {
1828a1b2c209SPyun YongHyeon m_freem(*m_head);
1829a1b2c209SPyun YongHyeon *m_head = NULL;
1830a1b2c209SPyun YongHyeon return (error);
1831c8befdd5SWarner Losh }
1832a1b2c209SPyun YongHyeon } else if (error != 0)
1833a1b2c209SPyun YongHyeon return (error);
1834a1b2c209SPyun YongHyeon if (nsegs == 0) {
1835a1b2c209SPyun YongHyeon m_freem(*m_head);
1836a1b2c209SPyun YongHyeon *m_head = NULL;
1837a1b2c209SPyun YongHyeon return (EIO);
1838a1b2c209SPyun YongHyeon }
1839a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, txc->ste_map,
1840a1b2c209SPyun YongHyeon BUS_DMASYNC_PREWRITE);
1841c8befdd5SWarner Losh
1842a1b2c209SPyun YongHyeon desc = txc->ste_ptr;
1843a1b2c209SPyun YongHyeon for (i = 0; i < nsegs; i++) {
1844a1b2c209SPyun YongHyeon frag = &desc->ste_frags[i];
1845a1b2c209SPyun YongHyeon frag->ste_addr = htole32(STE_ADDR_LO(txsegs[i].ds_addr));
1846a1b2c209SPyun YongHyeon frag->ste_len = htole32(txsegs[i].ds_len);
1847a1b2c209SPyun YongHyeon }
1848a1b2c209SPyun YongHyeon desc->ste_frags[i - 1].ste_len |= htole32(STE_FRAG_LAST);
1849c8befdd5SWarner Losh /*
1850a1b2c209SPyun YongHyeon * Because we use Tx polling we can't chain multiple
1851a1b2c209SPyun YongHyeon * Tx descriptors here. Otherwise we race with controller.
1852c8befdd5SWarner Losh */
1853a1b2c209SPyun YongHyeon desc->ste_next = 0;
1854ae49e7a6SPyun YongHyeon if ((sc->ste_cdata.ste_tx_prod % STE_TX_INTR_FRAMES) == 0)
1855ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS |
1856ae49e7a6SPyun YongHyeon STE_TXCTL_DMAINTR);
1857ae49e7a6SPyun YongHyeon else
1858ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS);
1859a1b2c209SPyun YongHyeon txc->ste_mbuf = *m_head;
1860a1b2c209SPyun YongHyeon STE_INC(sc->ste_cdata.ste_tx_prod, STE_TX_LIST_CNT);
1861a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt++;
1862c8befdd5SWarner Losh
1863c8befdd5SWarner Losh return (0);
1864c8befdd5SWarner Losh }
1865c8befdd5SWarner Losh
1866c8befdd5SWarner Losh static void
ste_start(if_t ifp)18676712df3aSJustin Hibbits ste_start(if_t ifp)
1868c8befdd5SWarner Losh {
1869c8befdd5SWarner Losh struct ste_softc *sc;
1870c8befdd5SWarner Losh
18716712df3aSJustin Hibbits sc = if_getsoftc(ifp);
1872c8befdd5SWarner Losh STE_LOCK(sc);
1873c8befdd5SWarner Losh ste_start_locked(ifp);
1874c8befdd5SWarner Losh STE_UNLOCK(sc);
1875c8befdd5SWarner Losh }
1876c8befdd5SWarner Losh
1877c8befdd5SWarner Losh static void
ste_start_locked(if_t ifp)18786712df3aSJustin Hibbits ste_start_locked(if_t ifp)
1879c8befdd5SWarner Losh {
1880c8befdd5SWarner Losh struct ste_softc *sc;
1881c8befdd5SWarner Losh struct ste_chain *cur_tx;
1882f2632c3bSPyun YongHyeon struct mbuf *m_head = NULL;
1883a1b2c209SPyun YongHyeon int enq;
1884c8befdd5SWarner Losh
18856712df3aSJustin Hibbits sc = if_getsoftc(ifp);
1886c8befdd5SWarner Losh STE_LOCK_ASSERT(sc);
1887c8befdd5SWarner Losh
18886712df3aSJustin Hibbits if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
18894465097bSPyun YongHyeon IFF_DRV_RUNNING || (sc->ste_flags & STE_FLAG_LINK) == 0)
1890c8befdd5SWarner Losh return;
1891c8befdd5SWarner Losh
18926712df3aSJustin Hibbits for (enq = 0; !if_sendq_empty(ifp);) {
1893a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == STE_TX_LIST_CNT - 1) {
1894c8befdd5SWarner Losh /*
1895a1b2c209SPyun YongHyeon * Controller may have cached copy of the last used
1896a1b2c209SPyun YongHyeon * next ptr so we have to reserve one TFD to avoid
1897a1b2c209SPyun YongHyeon * TFD overruns.
1898c8befdd5SWarner Losh */
18996712df3aSJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
1900c8befdd5SWarner Losh break;
1901c8befdd5SWarner Losh }
19026712df3aSJustin Hibbits m_head = if_dequeue(ifp);
1903c8befdd5SWarner Losh if (m_head == NULL)
1904c8befdd5SWarner Losh break;
1905a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[sc->ste_cdata.ste_tx_prod];
1906a1b2c209SPyun YongHyeon if (ste_encap(sc, &m_head, cur_tx) != 0) {
1907a1b2c209SPyun YongHyeon if (m_head == NULL)
1908c8befdd5SWarner Losh break;
19096712df3aSJustin Hibbits if_sendq_prepend(ifp, m_head);
1910a1b2c209SPyun YongHyeon break;
1911a1b2c209SPyun YongHyeon }
1912a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_last_tx == NULL) {
1913a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag,
1914a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map,
1915a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1916c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL);
1917c8befdd5SWarner Losh ste_wait(sc);
1918c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR,
1919a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_tx_list_paddr));
1920c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64);
1921c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL);
1922c8befdd5SWarner Losh ste_wait(sc);
1923c8befdd5SWarner Losh } else {
1924a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_ptr->ste_next =
1925a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_phys;
1926a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag,
1927a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map,
1928a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1929c8befdd5SWarner Losh }
1930a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx = cur_tx;
1931c8befdd5SWarner Losh
1932a1b2c209SPyun YongHyeon enq++;
1933c8befdd5SWarner Losh /*
1934c8befdd5SWarner Losh * If there's a BPF listener, bounce a copy of this frame
1935c8befdd5SWarner Losh * to him.
1936c8befdd5SWarner Losh */
1937a1b2c209SPyun YongHyeon BPF_MTAP(ifp, m_head);
1938c8befdd5SWarner Losh }
1939a1b2c209SPyun YongHyeon
1940a1b2c209SPyun YongHyeon if (enq > 0)
1941a1b2c209SPyun YongHyeon sc->ste_timer = STE_TX_TIMEOUT;
1942c8befdd5SWarner Losh }
1943c8befdd5SWarner Losh
1944c8befdd5SWarner Losh static void
ste_watchdog(struct ste_softc * sc)19457cf545d0SJohn Baldwin ste_watchdog(struct ste_softc *sc)
1946c8befdd5SWarner Losh {
19476712df3aSJustin Hibbits if_t ifp;
1948c8befdd5SWarner Losh
19497cf545d0SJohn Baldwin ifp = sc->ste_ifp;
19507cf545d0SJohn Baldwin STE_LOCK_ASSERT(sc);
1951c8befdd5SWarner Losh
195210f695eeSPyun YongHyeon if (sc->ste_timer == 0 || --sc->ste_timer)
195310f695eeSPyun YongHyeon return;
195410f695eeSPyun YongHyeon
1955c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1956c8befdd5SWarner Losh if_printf(ifp, "watchdog timeout\n");
1957c8befdd5SWarner Losh
1958c8befdd5SWarner Losh ste_txeof(sc);
195981598b3eSPyun YongHyeon ste_txeoc(sc);
1960a1b2c209SPyun YongHyeon ste_rxeof(sc, -1);
19616712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
1962c8befdd5SWarner Losh ste_init_locked(sc);
1963c8befdd5SWarner Losh
19646712df3aSJustin Hibbits if (!if_sendq_empty(ifp))
1965c8befdd5SWarner Losh ste_start_locked(ifp);
1966c8befdd5SWarner Losh }
1967c8befdd5SWarner Losh
1968c8befdd5SWarner Losh static int
ste_shutdown(device_t dev)196960270842SPyun YongHyeon ste_shutdown(device_t dev)
1970c8befdd5SWarner Losh {
1971b4c170e1SPyun YongHyeon
1972b4c170e1SPyun YongHyeon return (ste_suspend(dev));
1973b4c170e1SPyun YongHyeon }
1974b4c170e1SPyun YongHyeon
1975b4c170e1SPyun YongHyeon static int
ste_suspend(device_t dev)1976b4c170e1SPyun YongHyeon ste_suspend(device_t dev)
1977b4c170e1SPyun YongHyeon {
1978c8befdd5SWarner Losh struct ste_softc *sc;
1979c8befdd5SWarner Losh
1980c8befdd5SWarner Losh sc = device_get_softc(dev);
1981c8befdd5SWarner Losh
1982c8befdd5SWarner Losh STE_LOCK(sc);
1983c8befdd5SWarner Losh ste_stop(sc);
1984b4c170e1SPyun YongHyeon ste_setwol(sc);
1985b4c170e1SPyun YongHyeon STE_UNLOCK(sc);
1986b4c170e1SPyun YongHyeon
1987b4c170e1SPyun YongHyeon return (0);
1988b4c170e1SPyun YongHyeon }
1989b4c170e1SPyun YongHyeon
1990b4c170e1SPyun YongHyeon static int
ste_resume(device_t dev)1991b4c170e1SPyun YongHyeon ste_resume(device_t dev)
1992b4c170e1SPyun YongHyeon {
1993b4c170e1SPyun YongHyeon struct ste_softc *sc;
19946712df3aSJustin Hibbits if_t ifp;
1995b4c170e1SPyun YongHyeon
1996b4c170e1SPyun YongHyeon sc = device_get_softc(dev);
1997b4c170e1SPyun YongHyeon STE_LOCK(sc);
1998b4c170e1SPyun YongHyeon ifp = sc->ste_ifp;
19996712df3aSJustin Hibbits if ((if_getflags(ifp) & IFF_UP) != 0) {
20006712df3aSJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
2001b4c170e1SPyun YongHyeon ste_init_locked(sc);
2002b4c170e1SPyun YongHyeon }
2003c8befdd5SWarner Losh STE_UNLOCK(sc);
2004c8befdd5SWarner Losh
2005c8befdd5SWarner Losh return (0);
2006c8befdd5SWarner Losh }
20078657caa6SPyun YongHyeon
20088657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD32(c, h, n, p, d) \
20098657caa6SPyun YongHyeon SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
20108657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD64(c, h, n, p, d) \
20116dc7dc9aSMatthew D Fleming SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
20128657caa6SPyun YongHyeon
20138657caa6SPyun YongHyeon static void
ste_sysctl_node(struct ste_softc * sc)20148657caa6SPyun YongHyeon ste_sysctl_node(struct ste_softc *sc)
20158657caa6SPyun YongHyeon {
20168657caa6SPyun YongHyeon struct sysctl_ctx_list *ctx;
20178657caa6SPyun YongHyeon struct sysctl_oid_list *child, *parent;
20188657caa6SPyun YongHyeon struct sysctl_oid *tree;
20198657caa6SPyun YongHyeon struct ste_hw_stats *stats;
20208657caa6SPyun YongHyeon
20218657caa6SPyun YongHyeon stats = &sc->ste_stats;
20228657caa6SPyun YongHyeon ctx = device_get_sysctl_ctx(sc->ste_dev);
20238657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ste_dev));
20248657caa6SPyun YongHyeon
2025fabbaac5SPyun YongHyeon SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_rx_mod",
2026fabbaac5SPyun YongHyeon CTLFLAG_RW, &sc->ste_int_rx_mod, 0, "ste RX interrupt moderation");
2027fabbaac5SPyun YongHyeon /* Pull in device tunables. */
2028fabbaac5SPyun YongHyeon sc->ste_int_rx_mod = STE_IM_RX_TIMER_DEFAULT;
2029fabbaac5SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev),
2030fabbaac5SPyun YongHyeon device_get_unit(sc->ste_dev), "int_rx_mod", &sc->ste_int_rx_mod);
2031fabbaac5SPyun YongHyeon
20327029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats",
20337029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "STE statistics");
20348657caa6SPyun YongHyeon parent = SYSCTL_CHILDREN(tree);
20358657caa6SPyun YongHyeon
20368657caa6SPyun YongHyeon /* Rx statistics. */
20377029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx",
20387029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Rx MAC statistics");
20398657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree);
20408657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets",
20418657caa6SPyun YongHyeon &stats->rx_bytes, "Good octets");
20428657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames",
20438657caa6SPyun YongHyeon &stats->rx_frames, "Good frames");
20448657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames",
20458657caa6SPyun YongHyeon &stats->rx_bcast_frames, "Good broadcast frames");
20468657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames",
20478657caa6SPyun YongHyeon &stats->rx_mcast_frames, "Good multicast frames");
20488657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "lost_frames",
20498657caa6SPyun YongHyeon &stats->rx_lost_frames, "Lost frames");
20508657caa6SPyun YongHyeon
20518657caa6SPyun YongHyeon /* Tx statistics. */
20527029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx",
20537029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Tx MAC statistics");
20548657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree);
20558657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets",
20568657caa6SPyun YongHyeon &stats->tx_bytes, "Good octets");
20578657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames",
20588657caa6SPyun YongHyeon &stats->tx_frames, "Good frames");
20598657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames",
20608657caa6SPyun YongHyeon &stats->tx_bcast_frames, "Good broadcast frames");
20618657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames",
20628657caa6SPyun YongHyeon &stats->tx_mcast_frames, "Good multicast frames");
20638657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "carrier_errs",
20648657caa6SPyun YongHyeon &stats->tx_carrsense_errs, "Carrier sense errors");
20658657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "single_colls",
20668657caa6SPyun YongHyeon &stats->tx_single_colls, "Single collisions");
20678657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "multi_colls",
20688657caa6SPyun YongHyeon &stats->tx_multi_colls, "Multiple collisions");
20698657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "late_colls",
20708657caa6SPyun YongHyeon &stats->tx_late_colls, "Late collisions");
20718657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "defers",
20728657caa6SPyun YongHyeon &stats->tx_frames_defered, "Frames with deferrals");
20738657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "excess_defers",
20748657caa6SPyun YongHyeon &stats->tx_excess_defers, "Frames with excessive derferrals");
20758657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "abort",
20768657caa6SPyun YongHyeon &stats->tx_abort, "Aborted frames due to Excessive collisions");
20778657caa6SPyun YongHyeon }
20788657caa6SPyun YongHyeon
20798657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD32
20808657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD64
2081b4c170e1SPyun YongHyeon
2082b4c170e1SPyun YongHyeon static void
ste_setwol(struct ste_softc * sc)2083b4c170e1SPyun YongHyeon ste_setwol(struct ste_softc *sc)
2084b4c170e1SPyun YongHyeon {
20856712df3aSJustin Hibbits if_t ifp;
2086b4c170e1SPyun YongHyeon uint8_t val;
2087b4c170e1SPyun YongHyeon
2088b4c170e1SPyun YongHyeon STE_LOCK_ASSERT(sc);
2089b4c170e1SPyun YongHyeon
2090*ddaf6524SJohn Baldwin if (!pci_has_pm(sc->ste_dev)) {
2091b4c170e1SPyun YongHyeon /* Disable WOL. */
2092b4c170e1SPyun YongHyeon CSR_READ_1(sc, STE_WAKE_EVENT);
2093b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, 0);
2094b4c170e1SPyun YongHyeon return;
2095b4c170e1SPyun YongHyeon }
2096b4c170e1SPyun YongHyeon
2097b4c170e1SPyun YongHyeon ifp = sc->ste_ifp;
2098b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT);
2099b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB |
2100b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB);
21016712df3aSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
2102b4c170e1SPyun YongHyeon val |= STE_WAKEEVENT_MAGICPKT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB;
2103b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val);
2104b4c170e1SPyun YongHyeon /* Request PME. */
21056712df3aSJustin Hibbits if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
2106*ddaf6524SJohn Baldwin pci_enable_pme(sc->ste_dev);
2107b4c170e1SPyun YongHyeon }
2108