1c8befdd5SWarner Losh /*- 2c8befdd5SWarner Losh * Copyright (c) 1997, 1998, 1999 3c8befdd5SWarner Losh * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4c8befdd5SWarner Losh * 5c8befdd5SWarner Losh * Redistribution and use in source and binary forms, with or without 6c8befdd5SWarner Losh * modification, are permitted provided that the following conditions 7c8befdd5SWarner Losh * are met: 8c8befdd5SWarner Losh * 1. Redistributions of source code must retain the above copyright 9c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer. 10c8befdd5SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer in the 12c8befdd5SWarner Losh * documentation and/or other materials provided with the distribution. 13c8befdd5SWarner Losh * 3. All advertising materials mentioning features or use of this software 14c8befdd5SWarner Losh * must display the following acknowledgement: 15c8befdd5SWarner Losh * This product includes software developed by Bill Paul. 16c8befdd5SWarner Losh * 4. Neither the name of the author nor the names of any co-contributors 17c8befdd5SWarner Losh * may be used to endorse or promote products derived from this software 18c8befdd5SWarner Losh * without specific prior written permission. 19c8befdd5SWarner Losh * 20c8befdd5SWarner Losh * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21c8befdd5SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22c8befdd5SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23c8befdd5SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24c8befdd5SWarner Losh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25c8befdd5SWarner Losh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26c8befdd5SWarner Losh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27c8befdd5SWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28c8befdd5SWarner Losh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29c8befdd5SWarner Losh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30c8befdd5SWarner Losh * THE POSSIBILITY OF SUCH DAMAGE. 31c8befdd5SWarner Losh */ 32c8befdd5SWarner Losh 33c8befdd5SWarner Losh #include <sys/cdefs.h> 34c8befdd5SWarner Losh __FBSDID("$FreeBSD$"); 35c8befdd5SWarner Losh 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> 56c8befdd5SWarner Losh #include <net/if_arp.h> 57c8befdd5SWarner Losh #include <net/ethernet.h> 58c8befdd5SWarner Losh #include <net/if_dl.h> 59c8befdd5SWarner Losh #include <net/if_media.h> 60c8befdd5SWarner Losh #include <net/if_types.h> 61c8befdd5SWarner Losh #include <net/if_vlan_var.h> 62c8befdd5SWarner Losh 63c8befdd5SWarner Losh #include <machine/bus.h> 64c8befdd5SWarner Losh #include <machine/resource.h> 65c8befdd5SWarner Losh 66c8befdd5SWarner Losh #include <dev/mii/mii.h> 67c8befdd5SWarner Losh #include <dev/mii/miivar.h> 68c8befdd5SWarner Losh 69c8befdd5SWarner Losh #include <dev/pci/pcireg.h> 70c8befdd5SWarner Losh #include <dev/pci/pcivar.h> 71c8befdd5SWarner Losh 72a1b2c209SPyun YongHyeon #include <dev/ste/if_stereg.h> 73a1b2c209SPyun YongHyeon 74c8befdd5SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 75c8befdd5SWarner Losh #include "miibus_if.h" 76c8befdd5SWarner Losh 77c8befdd5SWarner Losh MODULE_DEPEND(ste, pci, 1, 1, 1); 78c8befdd5SWarner Losh MODULE_DEPEND(ste, ether, 1, 1, 1); 79c8befdd5SWarner Losh MODULE_DEPEND(ste, miibus, 1, 1, 1); 80c8befdd5SWarner Losh 8181598b3eSPyun YongHyeon /* Define to show Tx error status. */ 8281598b3eSPyun YongHyeon #define STE_SHOW_TXERRORS 8381598b3eSPyun YongHyeon 84c8befdd5SWarner Losh /* 85c8befdd5SWarner Losh * Various supported device vendors/types and their names. 86c8befdd5SWarner Losh */ 87c8befdd5SWarner Losh static struct ste_type ste_devs[] = { 88c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_1, "Sundance ST201 10/100BaseTX" }, 89c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_2, "Sundance ST201 10/100BaseTX" }, 90c8befdd5SWarner Losh { DL_VENDORID, DL_DEVICEID_DL10050, "D-Link DL10050 10/100BaseTX" }, 91c8befdd5SWarner Losh { 0, 0, NULL } 92c8befdd5SWarner Losh }; 93c8befdd5SWarner Losh 94c8befdd5SWarner Losh static int ste_attach(device_t); 95c8befdd5SWarner Losh static int ste_detach(device_t); 96084dc54bSPyun YongHyeon static int ste_probe(device_t); 97b4c170e1SPyun YongHyeon static int ste_resume(device_t); 98c8befdd5SWarner Losh static int ste_shutdown(device_t); 99b4c170e1SPyun YongHyeon static int ste_suspend(device_t); 100084dc54bSPyun YongHyeon 101a1b2c209SPyun YongHyeon static int ste_dma_alloc(struct ste_softc *); 102a1b2c209SPyun YongHyeon static void ste_dma_free(struct ste_softc *); 103a1b2c209SPyun YongHyeon static void ste_dmamap_cb(void *, bus_dma_segment_t *, int, int); 104084dc54bSPyun YongHyeon static int ste_eeprom_wait(struct ste_softc *); 105a1b2c209SPyun YongHyeon static int ste_encap(struct ste_softc *, struct mbuf **, 106a1b2c209SPyun YongHyeon struct ste_chain *); 107c8befdd5SWarner Losh static int ste_ifmedia_upd(struct ifnet *); 108c8befdd5SWarner Losh static void ste_ifmedia_sts(struct ifnet *, struct ifmediareq *); 109084dc54bSPyun YongHyeon static void ste_init(void *); 110084dc54bSPyun YongHyeon static void ste_init_locked(struct ste_softc *); 111c8befdd5SWarner Losh static int ste_init_rx_list(struct ste_softc *); 112c8befdd5SWarner Losh static void ste_init_tx_list(struct ste_softc *); 113084dc54bSPyun YongHyeon static void ste_intr(void *); 114084dc54bSPyun YongHyeon static int ste_ioctl(struct ifnet *, u_long, caddr_t); 115084dc54bSPyun YongHyeon static int ste_mii_readreg(struct ste_softc *, struct ste_mii_frame *); 116084dc54bSPyun YongHyeon static void ste_mii_send(struct ste_softc *, uint32_t, int); 117084dc54bSPyun YongHyeon static void ste_mii_sync(struct ste_softc *); 118084dc54bSPyun YongHyeon static int ste_mii_writereg(struct ste_softc *, struct ste_mii_frame *); 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 *); 129084dc54bSPyun YongHyeon static void ste_start(struct ifnet *); 130084dc54bSPyun YongHyeon static void ste_start_locked(struct ifnet *); 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 141c8befdd5SWarner Losh static device_method_t ste_methods[] = { 142c8befdd5SWarner Losh /* Device interface */ 143c8befdd5SWarner Losh DEVMETHOD(device_probe, ste_probe), 144c8befdd5SWarner Losh DEVMETHOD(device_attach, ste_attach), 145c8befdd5SWarner Losh DEVMETHOD(device_detach, ste_detach), 146c8befdd5SWarner Losh DEVMETHOD(device_shutdown, ste_shutdown), 147b4c170e1SPyun YongHyeon DEVMETHOD(device_suspend, ste_suspend), 148b4c170e1SPyun YongHyeon DEVMETHOD(device_resume, ste_resume), 149c8befdd5SWarner Losh 150c8befdd5SWarner Losh /* bus interface */ 151c8befdd5SWarner Losh DEVMETHOD(bus_print_child, bus_generic_print_child), 152c8befdd5SWarner Losh DEVMETHOD(bus_driver_added, bus_generic_driver_added), 153c8befdd5SWarner Losh 154c8befdd5SWarner Losh /* MII interface */ 155c8befdd5SWarner Losh DEVMETHOD(miibus_readreg, ste_miibus_readreg), 156c8befdd5SWarner Losh DEVMETHOD(miibus_writereg, ste_miibus_writereg), 157c8befdd5SWarner Losh DEVMETHOD(miibus_statchg, ste_miibus_statchg), 158c8befdd5SWarner Losh 159c8befdd5SWarner Losh { 0, 0 } 160c8befdd5SWarner Losh }; 161c8befdd5SWarner Losh 162c8befdd5SWarner Losh static driver_t ste_driver = { 163c8befdd5SWarner Losh "ste", 164c8befdd5SWarner Losh ste_methods, 165c8befdd5SWarner Losh sizeof(struct ste_softc) 166c8befdd5SWarner Losh }; 167c8befdd5SWarner Losh 168c8befdd5SWarner Losh static devclass_t ste_devclass; 169c8befdd5SWarner Losh 170c8befdd5SWarner Losh DRIVER_MODULE(ste, pci, ste_driver, ste_devclass, 0, 0); 171c8befdd5SWarner Losh DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); 172c8befdd5SWarner Losh 173c8befdd5SWarner Losh #define STE_SETBIT4(sc, reg, x) \ 174c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) 175c8befdd5SWarner Losh 176c8befdd5SWarner Losh #define STE_CLRBIT4(sc, reg, x) \ 177c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) 178c8befdd5SWarner Losh 179c8befdd5SWarner Losh #define STE_SETBIT2(sc, reg, x) \ 180c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x)) 181c8befdd5SWarner Losh 182c8befdd5SWarner Losh #define STE_CLRBIT2(sc, reg, x) \ 183c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x)) 184c8befdd5SWarner Losh 185c8befdd5SWarner Losh #define STE_SETBIT1(sc, reg, x) \ 186c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) 187c8befdd5SWarner Losh 188c8befdd5SWarner Losh #define STE_CLRBIT1(sc, reg, x) \ 189c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x)) 190c8befdd5SWarner Losh 191c8befdd5SWarner Losh 192c8befdd5SWarner Losh #define MII_SET(x) STE_SETBIT1(sc, STE_PHYCTL, x) 193c8befdd5SWarner Losh #define MII_CLR(x) STE_CLRBIT1(sc, STE_PHYCTL, x) 194c8befdd5SWarner Losh 195c8befdd5SWarner Losh /* 196c8befdd5SWarner Losh * Sync the PHYs by setting data bit and strobing the clock 32 times. 197c8befdd5SWarner Losh */ 198c8befdd5SWarner Losh static void 19960270842SPyun YongHyeon ste_mii_sync(struct ste_softc *sc) 200c8befdd5SWarner Losh { 20142306cb0SPyun YongHyeon int i; 202c8befdd5SWarner Losh 203c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR|STE_PHYCTL_MDATA); 204c8befdd5SWarner Losh 205c8befdd5SWarner Losh for (i = 0; i < 32; i++) { 206c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 207c8befdd5SWarner Losh DELAY(1); 208c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 209c8befdd5SWarner Losh DELAY(1); 210c8befdd5SWarner Losh } 211c8befdd5SWarner Losh } 212c8befdd5SWarner Losh 213c8befdd5SWarner Losh /* 214c8befdd5SWarner Losh * Clock a series of bits through the MII. 215c8befdd5SWarner Losh */ 216c8befdd5SWarner Losh static void 21756af54f2SPyun YongHyeon ste_mii_send(struct ste_softc *sc, uint32_t bits, int cnt) 218c8befdd5SWarner Losh { 219c8befdd5SWarner Losh int i; 220c8befdd5SWarner Losh 221c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 222c8befdd5SWarner Losh 223c8befdd5SWarner Losh for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 224c8befdd5SWarner Losh if (bits & i) { 225c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDATA); 226c8befdd5SWarner Losh } else { 227c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDATA); 228c8befdd5SWarner Losh } 229c8befdd5SWarner Losh DELAY(1); 230c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 231c8befdd5SWarner Losh DELAY(1); 232c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 233c8befdd5SWarner Losh } 234c8befdd5SWarner Losh } 235c8befdd5SWarner Losh 236c8befdd5SWarner Losh /* 237c8befdd5SWarner Losh * Read an PHY register through the MII. 238c8befdd5SWarner Losh */ 239c8befdd5SWarner Losh static int 24060270842SPyun YongHyeon ste_mii_readreg(struct ste_softc *sc, struct ste_mii_frame *frame) 241c8befdd5SWarner Losh { 242c8befdd5SWarner Losh int i, ack; 243c8befdd5SWarner Losh 244c8befdd5SWarner Losh /* 245c8befdd5SWarner Losh * Set up frame for RX. 246c8befdd5SWarner Losh */ 247c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 248c8befdd5SWarner Losh frame->mii_opcode = STE_MII_READOP; 249c8befdd5SWarner Losh frame->mii_turnaround = 0; 250c8befdd5SWarner Losh frame->mii_data = 0; 251c8befdd5SWarner Losh 252c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PHYCTL, 0); 253c8befdd5SWarner Losh /* 254c8befdd5SWarner Losh * Turn on data xmit. 255c8befdd5SWarner Losh */ 256c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 257c8befdd5SWarner Losh 258c8befdd5SWarner Losh ste_mii_sync(sc); 259c8befdd5SWarner Losh 260c8befdd5SWarner Losh /* 261c8befdd5SWarner Losh * Send command/address info. 262c8befdd5SWarner Losh */ 263c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 264c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 265c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 266c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 267c8befdd5SWarner Losh 268c8befdd5SWarner Losh /* Turn off xmit. */ 269c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 270c8befdd5SWarner Losh 271c8befdd5SWarner Losh /* Idle bit */ 272c8befdd5SWarner Losh MII_CLR((STE_PHYCTL_MCLK|STE_PHYCTL_MDATA)); 273c8befdd5SWarner Losh DELAY(1); 274c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 275c8befdd5SWarner Losh DELAY(1); 276c8befdd5SWarner Losh 277c8befdd5SWarner Losh /* Check for ack */ 278c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 279c8befdd5SWarner Losh DELAY(1); 280c8befdd5SWarner Losh ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; 281c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 282c8befdd5SWarner Losh DELAY(1); 283c8befdd5SWarner Losh 284c8befdd5SWarner Losh /* 285c8befdd5SWarner Losh * Now try reading data bits. If the ack failed, we still 286c8befdd5SWarner Losh * need to clock through 16 cycles to keep the PHY(s) in sync. 287c8befdd5SWarner Losh */ 288c8befdd5SWarner Losh if (ack) { 289c8befdd5SWarner Losh for (i = 0; i < 16; i++) { 290c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 291c8befdd5SWarner Losh DELAY(1); 292c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 293c8befdd5SWarner Losh DELAY(1); 294c8befdd5SWarner Losh } 295c8befdd5SWarner Losh goto fail; 296c8befdd5SWarner Losh } 297c8befdd5SWarner Losh 298c8befdd5SWarner Losh for (i = 0x8000; i; i >>= 1) { 299c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 300c8befdd5SWarner Losh DELAY(1); 301c8befdd5SWarner Losh if (!ack) { 302c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA) 303c8befdd5SWarner Losh frame->mii_data |= i; 304c8befdd5SWarner Losh DELAY(1); 305c8befdd5SWarner Losh } 306c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 307c8befdd5SWarner Losh DELAY(1); 308c8befdd5SWarner Losh } 309c8befdd5SWarner Losh 310c8befdd5SWarner Losh fail: 311c8befdd5SWarner Losh 312c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 313c8befdd5SWarner Losh DELAY(1); 314c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 315c8befdd5SWarner Losh DELAY(1); 316c8befdd5SWarner Losh 317c8befdd5SWarner Losh if (ack) 318c8befdd5SWarner Losh return (1); 319c8befdd5SWarner Losh return (0); 320c8befdd5SWarner Losh } 321c8befdd5SWarner Losh 322c8befdd5SWarner Losh /* 323c8befdd5SWarner Losh * Write to a PHY register through the MII. 324c8befdd5SWarner Losh */ 325c8befdd5SWarner Losh static int 32660270842SPyun YongHyeon ste_mii_writereg(struct ste_softc *sc, struct ste_mii_frame *frame) 327c8befdd5SWarner Losh { 328c8befdd5SWarner Losh 329c8befdd5SWarner Losh /* 330c8befdd5SWarner Losh * Set up frame for TX. 331c8befdd5SWarner Losh */ 332c8befdd5SWarner Losh 333c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 334c8befdd5SWarner Losh frame->mii_opcode = STE_MII_WRITEOP; 335c8befdd5SWarner Losh frame->mii_turnaround = STE_MII_TURNAROUND; 336c8befdd5SWarner Losh 337c8befdd5SWarner Losh /* 338c8befdd5SWarner Losh * Turn on data output. 339c8befdd5SWarner Losh */ 340c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 341c8befdd5SWarner Losh 342c8befdd5SWarner Losh ste_mii_sync(sc); 343c8befdd5SWarner Losh 344c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 345c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 346c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 347c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 348c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_turnaround, 2); 349c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_data, 16); 350c8befdd5SWarner Losh 351c8befdd5SWarner Losh /* Idle bit. */ 352c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 353c8befdd5SWarner Losh DELAY(1); 354c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 355c8befdd5SWarner Losh DELAY(1); 356c8befdd5SWarner Losh 357c8befdd5SWarner Losh /* 358c8befdd5SWarner Losh * Turn off xmit. 359c8befdd5SWarner Losh */ 360c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 361c8befdd5SWarner Losh 362c8befdd5SWarner Losh return (0); 363c8befdd5SWarner Losh } 364c8befdd5SWarner Losh 365c8befdd5SWarner Losh static int 36660270842SPyun YongHyeon ste_miibus_readreg(device_t dev, int phy, int reg) 367c8befdd5SWarner Losh { 368c8befdd5SWarner Losh struct ste_softc *sc; 369c8befdd5SWarner Losh struct ste_mii_frame frame; 370c8befdd5SWarner Losh 371c8befdd5SWarner Losh sc = device_get_softc(dev); 372c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 373c8befdd5SWarner Losh 374c8befdd5SWarner Losh frame.mii_phyaddr = phy; 375c8befdd5SWarner Losh frame.mii_regaddr = reg; 376c8befdd5SWarner Losh ste_mii_readreg(sc, &frame); 377c8befdd5SWarner Losh 378c8befdd5SWarner Losh return (frame.mii_data); 379c8befdd5SWarner Losh } 380c8befdd5SWarner Losh 381c8befdd5SWarner Losh static int 38260270842SPyun YongHyeon ste_miibus_writereg(device_t dev, int phy, int reg, int data) 383c8befdd5SWarner Losh { 384c8befdd5SWarner Losh struct ste_softc *sc; 385c8befdd5SWarner Losh struct ste_mii_frame frame; 386c8befdd5SWarner Losh 387c8befdd5SWarner Losh sc = device_get_softc(dev); 388c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 389c8befdd5SWarner Losh 390c8befdd5SWarner Losh frame.mii_phyaddr = phy; 391c8befdd5SWarner Losh frame.mii_regaddr = reg; 392c8befdd5SWarner Losh frame.mii_data = data; 393c8befdd5SWarner Losh 394c8befdd5SWarner Losh ste_mii_writereg(sc, &frame); 395c8befdd5SWarner Losh 396c8befdd5SWarner Losh return (0); 397c8befdd5SWarner Losh } 398c8befdd5SWarner Losh 399c8befdd5SWarner Losh static void 40060270842SPyun YongHyeon ste_miibus_statchg(device_t dev) 401c8befdd5SWarner Losh { 402c8befdd5SWarner Losh struct ste_softc *sc; 403c8befdd5SWarner Losh struct mii_data *mii; 40410f695eeSPyun YongHyeon struct ifnet *ifp; 40510f695eeSPyun YongHyeon uint16_t cfg; 406c8befdd5SWarner Losh 407c8befdd5SWarner Losh sc = device_get_softc(dev); 408c8befdd5SWarner Losh 409c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 41010f695eeSPyun YongHyeon ifp = sc->ste_ifp; 41110f695eeSPyun YongHyeon if (mii == NULL || ifp == NULL || 41210f695eeSPyun YongHyeon (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 41310f695eeSPyun YongHyeon return; 414c8befdd5SWarner Losh 41510f695eeSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK; 41610f695eeSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 41710f695eeSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) { 41810f695eeSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) { 41910f695eeSPyun YongHyeon case IFM_10_T: 42010f695eeSPyun YongHyeon case IFM_100_TX: 42110f695eeSPyun YongHyeon case IFM_100_FX: 42210f695eeSPyun YongHyeon case IFM_100_T4: 42310f695eeSPyun YongHyeon sc->ste_flags |= STE_FLAG_LINK; 42410f695eeSPyun YongHyeon default: 42510f695eeSPyun YongHyeon break; 42610f695eeSPyun YongHyeon } 42710f695eeSPyun YongHyeon } 42810f695eeSPyun YongHyeon 42910f695eeSPyun YongHyeon /* Program MACs with resolved speed/duplex/flow-control. */ 43010f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) != 0) { 43110f695eeSPyun YongHyeon cfg = CSR_READ_2(sc, STE_MACCTL0); 43210f695eeSPyun YongHyeon cfg &= ~(STE_MACCTL0_FLOWCTL_ENABLE | STE_MACCTL0_FULLDUPLEX); 43310f695eeSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 43410f695eeSPyun YongHyeon /* 43510f695eeSPyun YongHyeon * ST201 data sheet says driver should enable receiving 43610f695eeSPyun YongHyeon * MAC control frames bit of receive mode register to 43710f695eeSPyun YongHyeon * receive flow-control frames but the register has no 43810f695eeSPyun YongHyeon * such bits. In addition the controller has no ability 43910f695eeSPyun YongHyeon * to send pause frames so it should be handled in 44010f695eeSPyun YongHyeon * driver. Implementing pause timer handling in driver 44110f695eeSPyun YongHyeon * layer is not trivial, so don't enable flow-control 44210f695eeSPyun YongHyeon * here. 44310f695eeSPyun YongHyeon */ 44410f695eeSPyun YongHyeon cfg |= STE_MACCTL0_FULLDUPLEX; 44510f695eeSPyun YongHyeon } 44610f695eeSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL0, cfg); 447c8befdd5SWarner Losh } 448c8befdd5SWarner Losh } 449c8befdd5SWarner Losh 450c8befdd5SWarner Losh static int 45160270842SPyun YongHyeon ste_ifmedia_upd(struct ifnet *ifp) 452c8befdd5SWarner Losh { 453c8befdd5SWarner Losh struct ste_softc *sc; 454bfe051bdSPyun YongHyeon struct mii_data *mii; 455bfe051bdSPyun YongHyeon struct mii_softc *miisc; 456bfe051bdSPyun YongHyeon int error; 457c8befdd5SWarner Losh 458c8befdd5SWarner Losh sc = ifp->if_softc; 459c8befdd5SWarner Losh STE_LOCK(sc); 460c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 461c8befdd5SWarner Losh if (mii->mii_instance) { 462c8befdd5SWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 463c8befdd5SWarner Losh mii_phy_reset(miisc); 464c8befdd5SWarner Losh } 465bfe051bdSPyun YongHyeon error = mii_mediachg(mii); 466bfe051bdSPyun YongHyeon STE_UNLOCK(sc); 467bfe051bdSPyun YongHyeon 468bfe051bdSPyun YongHyeon return (error); 469c8befdd5SWarner Losh } 470c8befdd5SWarner Losh 471c8befdd5SWarner Losh static void 47260270842SPyun YongHyeon ste_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 473c8befdd5SWarner Losh { 474c8befdd5SWarner Losh struct ste_softc *sc; 475c8befdd5SWarner Losh struct mii_data *mii; 476c8befdd5SWarner Losh 477c8befdd5SWarner Losh sc = ifp->if_softc; 478c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 479c8befdd5SWarner Losh 480c8befdd5SWarner Losh STE_LOCK(sc); 4815b7e3118SPyun YongHyeon if ((ifp->if_flags & IFF_UP) == 0) { 4825b7e3118SPyun YongHyeon STE_UNLOCK(sc); 4835b7e3118SPyun YongHyeon return; 4845b7e3118SPyun YongHyeon } 485c8befdd5SWarner Losh mii_pollstat(mii); 486c8befdd5SWarner Losh ifmr->ifm_active = mii->mii_media_active; 487c8befdd5SWarner Losh ifmr->ifm_status = mii->mii_media_status; 488c8befdd5SWarner Losh STE_UNLOCK(sc); 489c8befdd5SWarner Losh } 490c8befdd5SWarner Losh 491c8befdd5SWarner Losh static void 49260270842SPyun YongHyeon ste_wait(struct ste_softc *sc) 493c8befdd5SWarner Losh { 49442306cb0SPyun YongHyeon int i; 495c8befdd5SWarner Losh 496c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 497c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) 498c8befdd5SWarner Losh break; 4991bf71544SPyun YongHyeon DELAY(1); 500c8befdd5SWarner Losh } 501c8befdd5SWarner Losh 502c8befdd5SWarner Losh if (i == STE_TIMEOUT) 503c8befdd5SWarner Losh device_printf(sc->ste_dev, "command never completed!\n"); 504c8befdd5SWarner Losh } 505c8befdd5SWarner Losh 506c8befdd5SWarner Losh /* 507c8befdd5SWarner Losh * The EEPROM is slow: give it time to come ready after issuing 508c8befdd5SWarner Losh * it a command. 509c8befdd5SWarner Losh */ 510c8befdd5SWarner Losh static int 51160270842SPyun YongHyeon ste_eeprom_wait(struct ste_softc *sc) 512c8befdd5SWarner Losh { 513c8befdd5SWarner Losh int i; 514c8befdd5SWarner Losh 515c8befdd5SWarner Losh DELAY(1000); 516c8befdd5SWarner Losh 517c8befdd5SWarner Losh for (i = 0; i < 100; i++) { 518c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) 519c8befdd5SWarner Losh DELAY(1000); 520c8befdd5SWarner Losh else 521c8befdd5SWarner Losh break; 522c8befdd5SWarner Losh } 523c8befdd5SWarner Losh 524c8befdd5SWarner Losh if (i == 100) { 525c8befdd5SWarner Losh device_printf(sc->ste_dev, "eeprom failed to come ready\n"); 526c8befdd5SWarner Losh return (1); 527c8befdd5SWarner Losh } 528c8befdd5SWarner Losh 529c8befdd5SWarner Losh return (0); 530c8befdd5SWarner Losh } 531c8befdd5SWarner Losh 532c8befdd5SWarner Losh /* 533c8befdd5SWarner Losh * Read a sequence of words from the EEPROM. Note that ethernet address 534c8befdd5SWarner Losh * data is stored in the EEPROM in network byte order. 535c8befdd5SWarner Losh */ 536c8befdd5SWarner Losh static int 537fcd8385eSPyun YongHyeon ste_read_eeprom(struct ste_softc *sc, uint16_t *dest, int off, int cnt) 538c8befdd5SWarner Losh { 539c8befdd5SWarner Losh int err = 0, i; 540c8befdd5SWarner Losh 541c8befdd5SWarner Losh if (ste_eeprom_wait(sc)) 542c8befdd5SWarner Losh return (1); 543c8befdd5SWarner Losh 544c8befdd5SWarner Losh for (i = 0; i < cnt; i++) { 545c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); 546c8befdd5SWarner Losh err = ste_eeprom_wait(sc); 547c8befdd5SWarner Losh if (err) 548c8befdd5SWarner Losh break; 549fcd8385eSPyun YongHyeon *dest = le16toh(CSR_READ_2(sc, STE_EEPROM_DATA)); 550fcd8385eSPyun YongHyeon dest++; 551c8befdd5SWarner Losh } 552c8befdd5SWarner Losh 553c8befdd5SWarner Losh return (err ? 1 : 0); 554c8befdd5SWarner Losh } 555c8befdd5SWarner Losh 556c8befdd5SWarner Losh static void 557931ec15aSPyun YongHyeon ste_rxfilter(struct ste_softc *sc) 558c8befdd5SWarner Losh { 559c8befdd5SWarner Losh struct ifnet *ifp; 560c8befdd5SWarner Losh struct ifmultiaddr *ifma; 561f2632c3bSPyun YongHyeon uint32_t hashes[2] = { 0, 0 }; 562931ec15aSPyun YongHyeon uint8_t rxcfg; 563f2632c3bSPyun YongHyeon int h; 564c8befdd5SWarner Losh 565931ec15aSPyun YongHyeon STE_LOCK_ASSERT(sc); 566931ec15aSPyun YongHyeon 567c8befdd5SWarner Losh ifp = sc->ste_ifp; 568931ec15aSPyun YongHyeon rxcfg = CSR_READ_1(sc, STE_RX_MODE); 569931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_UNICAST; 570931ec15aSPyun YongHyeon rxcfg &= ~(STE_RXMODE_ALLMULTI | STE_RXMODE_MULTIHASH | 571931ec15aSPyun YongHyeon STE_RXMODE_BROADCAST | STE_RXMODE_PROMISC); 572931ec15aSPyun YongHyeon if (ifp->if_flags & IFF_BROADCAST) 573931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_BROADCAST; 574931ec15aSPyun YongHyeon if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) { 575931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_ALLMULTI) != 0) 576931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_ALLMULTI; 577931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_PROMISC) != 0) 578931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_PROMISC; 579931ec15aSPyun YongHyeon goto chipit; 580c8befdd5SWarner Losh } 581c8befdd5SWarner Losh 582931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_MULTIHASH; 583931ec15aSPyun YongHyeon /* Now program new ones. */ 584eb956cd0SRobert Watson if_maddr_rlock(ifp); 585c8befdd5SWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 586c8befdd5SWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 587c8befdd5SWarner Losh continue; 588c8befdd5SWarner Losh h = ether_crc32_be(LLADDR((struct sockaddr_dl *) 589c8befdd5SWarner Losh ifma->ifma_addr), ETHER_ADDR_LEN) & 0x3F; 590c8befdd5SWarner Losh if (h < 32) 591c8befdd5SWarner Losh hashes[0] |= (1 << h); 592c8befdd5SWarner Losh else 593c8befdd5SWarner Losh hashes[1] |= (1 << (h - 32)); 594c8befdd5SWarner Losh } 595eb956cd0SRobert Watson if_maddr_runlock(ifp); 596c8befdd5SWarner Losh 597931ec15aSPyun YongHyeon chipit: 598c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); 599c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); 600c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); 601c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); 602931ec15aSPyun YongHyeon CSR_WRITE_1(sc, STE_RX_MODE, rxcfg); 603931ec15aSPyun YongHyeon CSR_READ_1(sc, STE_RX_MODE); 604c8befdd5SWarner Losh } 605c8befdd5SWarner Losh 606c8befdd5SWarner Losh #ifdef DEVICE_POLLING 607c8befdd5SWarner Losh static poll_handler_t ste_poll, ste_poll_locked; 608c8befdd5SWarner Losh 6091abcdbd1SAttilio Rao static int 610c8befdd5SWarner Losh ste_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 611c8befdd5SWarner Losh { 612c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 6131abcdbd1SAttilio Rao int rx_npkts = 0; 614c8befdd5SWarner Losh 615c8befdd5SWarner Losh STE_LOCK(sc); 616c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6171abcdbd1SAttilio Rao rx_npkts = ste_poll_locked(ifp, cmd, count); 618c8befdd5SWarner Losh STE_UNLOCK(sc); 6191abcdbd1SAttilio Rao return (rx_npkts); 620c8befdd5SWarner Losh } 621c8befdd5SWarner Losh 6221abcdbd1SAttilio Rao static int 623c8befdd5SWarner Losh ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) 624c8befdd5SWarner Losh { 625c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 6261abcdbd1SAttilio Rao int rx_npkts; 627c8befdd5SWarner Losh 628c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 629c8befdd5SWarner Losh 630a1b2c209SPyun YongHyeon rx_npkts = ste_rxeof(sc, count); 631c8befdd5SWarner Losh ste_txeof(sc); 63281598b3eSPyun YongHyeon ste_txeoc(sc); 633c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 634c8befdd5SWarner Losh ste_start_locked(ifp); 635c8befdd5SWarner Losh 636c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) { 63756af54f2SPyun YongHyeon uint16_t status; 638c8befdd5SWarner Losh 639c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 640c8befdd5SWarner Losh 64110f695eeSPyun YongHyeon if (status & STE_ISR_STATS_OFLOW) 642c8befdd5SWarner Losh ste_stats_update(sc); 643c8befdd5SWarner Losh 64455d7003eSPyun YongHyeon if (status & STE_ISR_HOSTERR) { 64555d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 646c8befdd5SWarner Losh ste_init_locked(sc); 647c8befdd5SWarner Losh } 64855d7003eSPyun YongHyeon } 6491abcdbd1SAttilio Rao return (rx_npkts); 650c8befdd5SWarner Losh } 651c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 652c8befdd5SWarner Losh 653c8befdd5SWarner Losh static void 65460270842SPyun YongHyeon ste_intr(void *xsc) 655c8befdd5SWarner Losh { 656c8befdd5SWarner Losh struct ste_softc *sc; 657c8befdd5SWarner Losh struct ifnet *ifp; 658fabbaac5SPyun YongHyeon uint16_t intrs, status; 659c8befdd5SWarner Losh 660c8befdd5SWarner Losh sc = xsc; 661c8befdd5SWarner Losh STE_LOCK(sc); 662c8befdd5SWarner Losh ifp = sc->ste_ifp; 663c8befdd5SWarner Losh 664c8befdd5SWarner Losh #ifdef DEVICE_POLLING 665c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 666c8befdd5SWarner Losh STE_UNLOCK(sc); 667c8befdd5SWarner Losh return; 668c8befdd5SWarner Losh } 669c8befdd5SWarner Losh #endif 670fabbaac5SPyun YongHyeon /* Reading STE_ISR_ACK clears STE_IMR register. */ 671fabbaac5SPyun YongHyeon status = CSR_READ_2(sc, STE_ISR_ACK); 672fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 673c8befdd5SWarner Losh STE_UNLOCK(sc); 674c8befdd5SWarner Losh return; 675c8befdd5SWarner Losh } 676c8befdd5SWarner Losh 677fabbaac5SPyun YongHyeon intrs = STE_INTRS; 678fabbaac5SPyun YongHyeon if (status == 0xFFFF || (status & intrs) == 0) 679fabbaac5SPyun YongHyeon goto done; 680c8befdd5SWarner Losh 681fabbaac5SPyun YongHyeon if (sc->ste_int_rx_act > 0) { 682fabbaac5SPyun YongHyeon status &= ~STE_ISR_RX_DMADONE; 683fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE; 684fabbaac5SPyun YongHyeon } 685c8befdd5SWarner Losh 686fabbaac5SPyun YongHyeon if ((status & (STE_ISR_SOFTINTR | STE_ISR_RX_DMADONE)) != 0) { 687a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 688fabbaac5SPyun YongHyeon /* 689fabbaac5SPyun YongHyeon * The controller has no ability to Rx interrupt 690fabbaac5SPyun YongHyeon * moderation feature. Receiving 64 bytes frames 691fabbaac5SPyun YongHyeon * from wire generates too many interrupts which in 692fabbaac5SPyun YongHyeon * turn make system useless to process other useful 693fabbaac5SPyun YongHyeon * things. Fortunately ST201 supports single shot 694fabbaac5SPyun YongHyeon * timer so use the timer to implement Rx interrupt 695fabbaac5SPyun YongHyeon * moderation in driver. This adds more register 696fabbaac5SPyun YongHyeon * access but it greatly reduces number of Rx 697fabbaac5SPyun YongHyeon * interrupts under high network load. 698fabbaac5SPyun YongHyeon */ 699fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 700fabbaac5SPyun YongHyeon (sc->ste_int_rx_mod != 0)) { 701fabbaac5SPyun YongHyeon if ((status & STE_ISR_RX_DMADONE) != 0) { 702fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 703fabbaac5SPyun YongHyeon STE_TIMER_USECS(sc->ste_int_rx_mod)); 704fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE; 705fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 1; 706fabbaac5SPyun YongHyeon } else { 707fabbaac5SPyun YongHyeon intrs |= STE_IMR_RX_DMADONE; 708fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0; 709fabbaac5SPyun YongHyeon } 710fabbaac5SPyun YongHyeon } 711fabbaac5SPyun YongHyeon } 712fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 713fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DMADONE) != 0) 714c8befdd5SWarner Losh ste_txeof(sc); 715fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DONE) != 0) 716c8befdd5SWarner Losh ste_txeoc(sc); 717fabbaac5SPyun YongHyeon if ((status & STE_ISR_STATS_OFLOW) != 0) 718c8befdd5SWarner Losh ste_stats_update(sc); 719fabbaac5SPyun YongHyeon if ((status & STE_ISR_HOSTERR) != 0) { 72055d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 721fabbaac5SPyun YongHyeon ste_init_locked(sc); 722fabbaac5SPyun YongHyeon STE_UNLOCK(sc); 723fabbaac5SPyun YongHyeon return; 72455d7003eSPyun YongHyeon } 725c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 726c8befdd5SWarner Losh ste_start_locked(ifp); 727fabbaac5SPyun YongHyeon done: 728fabbaac5SPyun YongHyeon /* Re-enable interrupts */ 729fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, intrs); 730fabbaac5SPyun YongHyeon } 731c8befdd5SWarner Losh STE_UNLOCK(sc); 732c8befdd5SWarner Losh } 733c8befdd5SWarner Losh 734c8befdd5SWarner Losh /* 735c8befdd5SWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 736c8befdd5SWarner Losh * the higher level protocols. 737c8befdd5SWarner Losh */ 7381abcdbd1SAttilio Rao static int 739a1b2c209SPyun YongHyeon ste_rxeof(struct ste_softc *sc, int count) 740c8befdd5SWarner Losh { 741c8befdd5SWarner Losh struct mbuf *m; 742c8befdd5SWarner Losh struct ifnet *ifp; 743c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx; 74456af54f2SPyun YongHyeon uint32_t rxstat; 745a1b2c209SPyun YongHyeon int total_len, rx_npkts; 746c8befdd5SWarner Losh 747c8befdd5SWarner Losh ifp = sc->ste_ifp; 748c8befdd5SWarner Losh 749a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 750a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 751a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 752c8befdd5SWarner Losh 753c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head; 754a1b2c209SPyun YongHyeon for (rx_npkts = 0; rx_npkts < STE_RX_LIST_CNT; rx_npkts++, 755a1b2c209SPyun YongHyeon cur_rx = cur_rx->ste_next) { 756a1b2c209SPyun YongHyeon rxstat = le32toh(cur_rx->ste_ptr->ste_status); 757a1b2c209SPyun YongHyeon if ((rxstat & STE_RXSTAT_DMADONE) == 0) 758a1b2c209SPyun YongHyeon break; 759a1b2c209SPyun YongHyeon #ifdef DEVICE_POLLING 760a1b2c209SPyun YongHyeon if (ifp->if_capenable & IFCAP_POLLING) { 761a1b2c209SPyun YongHyeon if (count == 0) 762a1b2c209SPyun YongHyeon break; 763a1b2c209SPyun YongHyeon count--; 764a1b2c209SPyun YongHyeon } 765a1b2c209SPyun YongHyeon #endif 766a1b2c209SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 767a1b2c209SPyun YongHyeon break; 768c8befdd5SWarner Losh /* 769c8befdd5SWarner Losh * If an error occurs, update stats, clear the 770c8befdd5SWarner Losh * status word and leave the mbuf cluster in place: 771c8befdd5SWarner Losh * it should simply get re-used next time this descriptor 772c8befdd5SWarner Losh * comes up in the ring. 773c8befdd5SWarner Losh */ 774c8befdd5SWarner Losh if (rxstat & STE_RXSTAT_FRAME_ERR) { 775c8befdd5SWarner Losh ifp->if_ierrors++; 776c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 777c8befdd5SWarner Losh continue; 778c8befdd5SWarner Losh } 779c8befdd5SWarner Losh 780c8befdd5SWarner Losh /* No errors; receive the packet. */ 781c8befdd5SWarner Losh m = cur_rx->ste_mbuf; 782a1b2c209SPyun YongHyeon total_len = STE_RX_BYTES(rxstat); 783c8befdd5SWarner Losh 784c8befdd5SWarner Losh /* 785c8befdd5SWarner Losh * Try to conjure up a new mbuf cluster. If that 786c8befdd5SWarner Losh * fails, it means we have an out of memory condition and 787c8befdd5SWarner Losh * should leave the buffer in place and continue. This will 788c8befdd5SWarner Losh * result in a lost packet, but there's little else we 789c8befdd5SWarner Losh * can do in this situation. 790c8befdd5SWarner Losh */ 791a1b2c209SPyun YongHyeon if (ste_newbuf(sc, cur_rx) != 0) { 792da57a8c8SPyun YongHyeon ifp->if_iqdrops++; 793c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 794c8befdd5SWarner Losh continue; 795c8befdd5SWarner Losh } 796c8befdd5SWarner Losh 797c8befdd5SWarner Losh m->m_pkthdr.rcvif = ifp; 798c8befdd5SWarner Losh m->m_pkthdr.len = m->m_len = total_len; 799c8befdd5SWarner Losh 800c8befdd5SWarner Losh ifp->if_ipackets++; 801c8befdd5SWarner Losh STE_UNLOCK(sc); 802c8befdd5SWarner Losh (*ifp->if_input)(ifp, m); 803c8befdd5SWarner Losh STE_LOCK(sc); 804a1b2c209SPyun YongHyeon } 805c8befdd5SWarner Losh 806a1b2c209SPyun YongHyeon if (rx_npkts > 0) { 807a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_head = cur_rx; 808a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 809a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 810a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 811c8befdd5SWarner Losh } 812c8befdd5SWarner Losh 8131abcdbd1SAttilio Rao return (rx_npkts); 814c8befdd5SWarner Losh } 815c8befdd5SWarner Losh 816c8befdd5SWarner Losh static void 81760270842SPyun YongHyeon ste_txeoc(struct ste_softc *sc) 818c8befdd5SWarner Losh { 81981598b3eSPyun YongHyeon uint16_t txstat; 820c8befdd5SWarner Losh struct ifnet *ifp; 82181598b3eSPyun YongHyeon 82281598b3eSPyun YongHyeon STE_LOCK_ASSERT(sc); 823c8befdd5SWarner Losh 824c8befdd5SWarner Losh ifp = sc->ste_ifp; 825c8befdd5SWarner Losh 82681598b3eSPyun YongHyeon /* 82781598b3eSPyun YongHyeon * STE_TX_STATUS register implements a queue of up to 31 82881598b3eSPyun YongHyeon * transmit status byte. Writing an arbitrary value to the 82981598b3eSPyun YongHyeon * register will advance the queue to the next transmit 83081598b3eSPyun YongHyeon * status byte. This means if driver does not read 83181598b3eSPyun YongHyeon * STE_TX_STATUS register after completing sending more 83281598b3eSPyun YongHyeon * than 31 frames the controller would be stalled so driver 83381598b3eSPyun YongHyeon * should re-wake the Tx MAC. This is the most severe 83481598b3eSPyun YongHyeon * limitation of ST201 based controller. 83581598b3eSPyun YongHyeon */ 83681598b3eSPyun YongHyeon for (;;) { 83781598b3eSPyun YongHyeon txstat = CSR_READ_2(sc, STE_TX_STATUS); 83881598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_TXDONE) == 0) 83981598b3eSPyun YongHyeon break; 84081598b3eSPyun YongHyeon if ((txstat & (STE_TXSTATUS_UNDERRUN | 84181598b3eSPyun YongHyeon STE_TXSTATUS_EXCESSCOLLS | STE_TXSTATUS_RECLAIMERR | 84281598b3eSPyun YongHyeon STE_TXSTATUS_STATSOFLOW)) != 0) { 843c8befdd5SWarner Losh ifp->if_oerrors++; 84481598b3eSPyun YongHyeon #ifdef STE_SHOW_TXERRORS 84581598b3eSPyun YongHyeon device_printf(sc->ste_dev, "TX error : 0x%b\n", 84681598b3eSPyun YongHyeon txstat & 0xFF, STE_ERR_BITS); 84781598b3eSPyun YongHyeon #endif 84881598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_UNDERRUN) != 0 && 849c8befdd5SWarner Losh sc->ste_tx_thresh < STE_PACKET_SIZE) { 850c8befdd5SWarner Losh sc->ste_tx_thresh += STE_MIN_FRAMELEN; 85181598b3eSPyun YongHyeon if (sc->ste_tx_thresh > STE_PACKET_SIZE) 85281598b3eSPyun YongHyeon sc->ste_tx_thresh = STE_PACKET_SIZE; 853c8befdd5SWarner Losh device_printf(sc->ste_dev, 85481598b3eSPyun YongHyeon "TX underrun, increasing TX" 855c8befdd5SWarner Losh " start threshold to %d bytes\n", 856c8befdd5SWarner Losh sc->ste_tx_thresh); 85781598b3eSPyun YongHyeon /* Make sure to disable active DMA cycles. */ 85881598b3eSPyun YongHyeon STE_SETBIT4(sc, STE_DMACTL, 85981598b3eSPyun YongHyeon STE_DMACTL_TXDMA_STALL); 86081598b3eSPyun YongHyeon ste_wait(sc); 86155d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 862c8befdd5SWarner Losh ste_init_locked(sc); 86381598b3eSPyun YongHyeon break; 86481598b3eSPyun YongHyeon } 86581598b3eSPyun YongHyeon /* Restart Tx. */ 86681598b3eSPyun YongHyeon ste_restart_tx(sc); 86781598b3eSPyun YongHyeon } 86881598b3eSPyun YongHyeon /* 86981598b3eSPyun YongHyeon * Advance to next status and ACK TxComplete 87081598b3eSPyun YongHyeon * interrupt. ST201 data sheet was wrong here, to 87181598b3eSPyun YongHyeon * get next Tx status, we have to write both 87281598b3eSPyun YongHyeon * STE_TX_STATUS and STE_TX_FRAMEID register. 87381598b3eSPyun YongHyeon * Otherwise controller returns the same status 87481598b3eSPyun YongHyeon * as well as not acknowledge Tx completion 87581598b3eSPyun YongHyeon * interrupt. 87681598b3eSPyun YongHyeon */ 877c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STATUS, txstat); 878c8befdd5SWarner Losh } 879c8befdd5SWarner Losh } 880c8befdd5SWarner Losh 881c8befdd5SWarner Losh static void 88210f695eeSPyun YongHyeon ste_tick(void *arg) 88310f695eeSPyun YongHyeon { 88410f695eeSPyun YongHyeon struct ste_softc *sc; 88510f695eeSPyun YongHyeon struct mii_data *mii; 88610f695eeSPyun YongHyeon 88710f695eeSPyun YongHyeon sc = (struct ste_softc *)arg; 88810f695eeSPyun YongHyeon 88910f695eeSPyun YongHyeon STE_LOCK_ASSERT(sc); 89010f695eeSPyun YongHyeon 89110f695eeSPyun YongHyeon mii = device_get_softc(sc->ste_miibus); 89210f695eeSPyun YongHyeon mii_tick(mii); 89310f695eeSPyun YongHyeon /* 89410f695eeSPyun YongHyeon * ukphy(4) does not seem to generate CB that reports 89510f695eeSPyun YongHyeon * resolved link state so if we know we lost a link, 89610f695eeSPyun YongHyeon * explicitly check the link state. 89710f695eeSPyun YongHyeon */ 89810f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) == 0) 89910f695eeSPyun YongHyeon ste_miibus_statchg(sc->ste_dev); 900ae49e7a6SPyun YongHyeon /* 901ae49e7a6SPyun YongHyeon * Because we are not generating Tx completion 902ae49e7a6SPyun YongHyeon * interrupt for every frame, reclaim transmitted 903ae49e7a6SPyun YongHyeon * buffers here. 904ae49e7a6SPyun YongHyeon */ 905ae49e7a6SPyun YongHyeon ste_txeof(sc); 906ae49e7a6SPyun YongHyeon ste_txeoc(sc); 90710f695eeSPyun YongHyeon ste_stats_update(sc); 90810f695eeSPyun YongHyeon ste_watchdog(sc); 90910f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc); 91010f695eeSPyun YongHyeon } 91110f695eeSPyun YongHyeon 91210f695eeSPyun YongHyeon static void 91360270842SPyun YongHyeon ste_txeof(struct ste_softc *sc) 914c8befdd5SWarner Losh { 915c8befdd5SWarner Losh struct ifnet *ifp; 916f2632c3bSPyun YongHyeon struct ste_chain *cur_tx; 917a1b2c209SPyun YongHyeon uint32_t txstat; 918c8befdd5SWarner Losh int idx; 919c8befdd5SWarner Losh 920a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 921c8befdd5SWarner Losh 922a1b2c209SPyun YongHyeon ifp = sc->ste_ifp; 923c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_cons; 924a1b2c209SPyun YongHyeon if (idx == sc->ste_cdata.ste_tx_prod) 925a1b2c209SPyun YongHyeon return; 926a1b2c209SPyun YongHyeon 927a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 928a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 929a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 930a1b2c209SPyun YongHyeon 931c8befdd5SWarner Losh while (idx != sc->ste_cdata.ste_tx_prod) { 932c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; 933a1b2c209SPyun YongHyeon txstat = le32toh(cur_tx->ste_ptr->ste_ctl); 934a1b2c209SPyun YongHyeon if ((txstat & STE_TXCTL_DMADONE) == 0) 935c8befdd5SWarner Losh break; 936a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map, 937a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTWRITE); 938a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map); 939a1b2c209SPyun YongHyeon KASSERT(cur_tx->ste_mbuf != NULL, 940a1b2c209SPyun YongHyeon ("%s: freeing NULL mbuf!\n", __func__)); 941c8befdd5SWarner Losh m_freem(cur_tx->ste_mbuf); 942c8befdd5SWarner Losh cur_tx->ste_mbuf = NULL; 943c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 944c8befdd5SWarner Losh ifp->if_opackets++; 945a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt--; 946c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT); 947c8befdd5SWarner Losh } 948c8befdd5SWarner Losh 949c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons = idx; 950a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == 0) 9517cf545d0SJohn Baldwin sc->ste_timer = 0; 952c8befdd5SWarner Losh } 953c8befdd5SWarner Losh 954c8befdd5SWarner Losh static void 9558657caa6SPyun YongHyeon ste_stats_clear(struct ste_softc *sc) 9568657caa6SPyun YongHyeon { 9578657caa6SPyun YongHyeon 9588657caa6SPyun YongHyeon STE_LOCK_ASSERT(sc); 9598657caa6SPyun YongHyeon 9608657caa6SPyun YongHyeon /* Rx stats. */ 9618657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO); 9628657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI); 9638657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_FRAMES); 9648657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_BCAST); 9658657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_MCAST); 9668657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_LOST); 9678657caa6SPyun YongHyeon /* Tx stats. */ 9688657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO); 9698657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI); 9708657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_FRAMES); 9718657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_BCAST); 9728657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_MCAST); 9738657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_CARRIER_ERR); 9748657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_SINGLE_COLLS); 9758657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_MULTI_COLLS); 9768657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_LATE_COLLS); 9778657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_DEFER); 9788657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_EXDEFER); 9798657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_ABORT); 9808657caa6SPyun YongHyeon } 9818657caa6SPyun YongHyeon 9828657caa6SPyun YongHyeon static void 98310f695eeSPyun YongHyeon ste_stats_update(struct ste_softc *sc) 984c8befdd5SWarner Losh { 985c8befdd5SWarner Losh struct ifnet *ifp; 9868657caa6SPyun YongHyeon struct ste_hw_stats *stats; 9878657caa6SPyun YongHyeon uint32_t val; 988c8befdd5SWarner Losh 989c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 990c8befdd5SWarner Losh 991c8befdd5SWarner Losh ifp = sc->ste_ifp; 9928657caa6SPyun YongHyeon stats = &sc->ste_stats; 9938657caa6SPyun YongHyeon /* Rx stats. */ 9948657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO) | 9958657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI)) << 16; 9968657caa6SPyun YongHyeon val &= 0x000FFFFF; 9978657caa6SPyun YongHyeon stats->rx_bytes += val; 9988657caa6SPyun YongHyeon stats->rx_frames += CSR_READ_2(sc, STE_STAT_RX_FRAMES); 9998657caa6SPyun YongHyeon stats->rx_bcast_frames += CSR_READ_1(sc, STE_STAT_RX_BCAST); 10008657caa6SPyun YongHyeon stats->rx_mcast_frames += CSR_READ_1(sc, STE_STAT_RX_MCAST); 10018657caa6SPyun YongHyeon stats->rx_lost_frames += CSR_READ_1(sc, STE_STAT_RX_LOST); 10028657caa6SPyun YongHyeon /* Tx stats. */ 10038657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO) | 10048657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI)) << 16; 10058657caa6SPyun YongHyeon val &= 0x000FFFFF; 10068657caa6SPyun YongHyeon stats->tx_bytes += val; 10078657caa6SPyun YongHyeon stats->tx_frames += CSR_READ_2(sc, STE_STAT_TX_FRAMES); 10088657caa6SPyun YongHyeon stats->tx_bcast_frames += CSR_READ_1(sc, STE_STAT_TX_BCAST); 10098657caa6SPyun YongHyeon stats->tx_mcast_frames += CSR_READ_1(sc, STE_STAT_TX_MCAST); 10108657caa6SPyun YongHyeon stats->tx_carrsense_errs += CSR_READ_1(sc, STE_STAT_CARRIER_ERR); 10118657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_SINGLE_COLLS); 10128657caa6SPyun YongHyeon stats->tx_single_colls += val; 10138657caa6SPyun YongHyeon ifp->if_collisions += val; 10148657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_MULTI_COLLS); 10158657caa6SPyun YongHyeon stats->tx_multi_colls += val; 10168657caa6SPyun YongHyeon ifp->if_collisions += val; 10178657caa6SPyun YongHyeon val += CSR_READ_1(sc, STE_STAT_LATE_COLLS); 10188657caa6SPyun YongHyeon stats->tx_late_colls += val; 10198657caa6SPyun YongHyeon ifp->if_collisions += val; 10208657caa6SPyun YongHyeon stats->tx_frames_defered += CSR_READ_1(sc, STE_STAT_TX_DEFER); 10218657caa6SPyun YongHyeon stats->tx_excess_defers += CSR_READ_1(sc, STE_STAT_TX_EXDEFER); 10228657caa6SPyun YongHyeon stats->tx_abort += CSR_READ_1(sc, STE_STAT_TX_ABORT); 1023c8befdd5SWarner Losh } 1024c8befdd5SWarner Losh 1025c8befdd5SWarner Losh /* 1026c8befdd5SWarner Losh * Probe for a Sundance ST201 chip. Check the PCI vendor and device 1027c8befdd5SWarner Losh * IDs against our list and return a device name if we find a match. 1028c8befdd5SWarner Losh */ 1029c8befdd5SWarner Losh static int 103060270842SPyun YongHyeon ste_probe(device_t dev) 1031c8befdd5SWarner Losh { 1032c8befdd5SWarner Losh struct ste_type *t; 1033c8befdd5SWarner Losh 1034c8befdd5SWarner Losh t = ste_devs; 1035c8befdd5SWarner Losh 1036c8befdd5SWarner Losh while (t->ste_name != NULL) { 1037c8befdd5SWarner Losh if ((pci_get_vendor(dev) == t->ste_vid) && 1038c8befdd5SWarner Losh (pci_get_device(dev) == t->ste_did)) { 1039c8befdd5SWarner Losh device_set_desc(dev, t->ste_name); 1040c8befdd5SWarner Losh return (BUS_PROBE_DEFAULT); 1041c8befdd5SWarner Losh } 1042c8befdd5SWarner Losh t++; 1043c8befdd5SWarner Losh } 1044c8befdd5SWarner Losh 1045c8befdd5SWarner Losh return (ENXIO); 1046c8befdd5SWarner Losh } 1047c8befdd5SWarner Losh 1048c8befdd5SWarner Losh /* 1049c8befdd5SWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 1050c8befdd5SWarner Losh * setup and ethernet/BPF attach. 1051c8befdd5SWarner Losh */ 1052c8befdd5SWarner Losh static int 105360270842SPyun YongHyeon ste_attach(device_t dev) 1054c8befdd5SWarner Losh { 1055c8befdd5SWarner Losh struct ste_softc *sc; 1056c8befdd5SWarner Losh struct ifnet *ifp; 1057fcd8385eSPyun YongHyeon uint16_t eaddr[ETHER_ADDR_LEN / 2]; 10588e5d93dbSMarius Strobl int error = 0, phy, pmc, prefer_iomap, rid; 1059c8befdd5SWarner Losh 1060c8befdd5SWarner Losh sc = device_get_softc(dev); 1061c8befdd5SWarner Losh sc->ste_dev = dev; 1062c8befdd5SWarner Losh 1063c8befdd5SWarner Losh /* 1064c8befdd5SWarner Losh * Only use one PHY since this chip reports multiple 1065c8befdd5SWarner Losh * Note on the DFE-550 the PHY is at 1 on the DFE-580 1066c8befdd5SWarner Losh * it is at 0 & 1. It is rev 0x12. 1067c8befdd5SWarner Losh */ 1068c8befdd5SWarner Losh if (pci_get_vendor(dev) == DL_VENDORID && 1069c8befdd5SWarner Losh pci_get_device(dev) == DL_DEVICEID_DL10050 && 1070c8befdd5SWarner Losh pci_get_revid(dev) == 0x12 ) 10714465097bSPyun YongHyeon sc->ste_flags |= STE_FLAG_ONE_PHY; 1072c8befdd5SWarner Losh 1073c8befdd5SWarner Losh mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 1074c8befdd5SWarner Losh MTX_DEF); 1075c8befdd5SWarner Losh /* 1076c8befdd5SWarner Losh * Map control/status registers. 1077c8befdd5SWarner Losh */ 1078c8befdd5SWarner Losh pci_enable_busmaster(dev); 1079c8befdd5SWarner Losh 1080497ffa52SPyun YongHyeon /* 1081497ffa52SPyun YongHyeon * Prefer memory space register mapping over IO space but use 1082497ffa52SPyun YongHyeon * IO space for a device that is known to have issues on memory 1083497ffa52SPyun YongHyeon * mapping. 1084497ffa52SPyun YongHyeon */ 1085497ffa52SPyun YongHyeon prefer_iomap = 0; 1086497ffa52SPyun YongHyeon if (pci_get_device(dev) == ST_DEVICEID_ST201_1) 1087497ffa52SPyun YongHyeon prefer_iomap = 1; 1088497ffa52SPyun YongHyeon else 1089497ffa52SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev), 1090497ffa52SPyun YongHyeon device_get_unit(sc->ste_dev), "prefer_iomap", 1091497ffa52SPyun YongHyeon &prefer_iomap); 1092497ffa52SPyun YongHyeon if (prefer_iomap == 0) { 1093c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(1); 1094c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_MEMORY; 1095c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type, 1096c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE); 1097497ffa52SPyun YongHyeon } 1098497ffa52SPyun YongHyeon if (prefer_iomap || sc->ste_res == NULL) { 1099c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(0); 1100c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_IOPORT; 1101c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type, 1102c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE); 1103c0270e60SPyun YongHyeon } 1104c8befdd5SWarner Losh if (sc->ste_res == NULL) { 1105c8befdd5SWarner Losh device_printf(dev, "couldn't map ports/memory\n"); 1106c8befdd5SWarner Losh error = ENXIO; 1107c8befdd5SWarner Losh goto fail; 1108c8befdd5SWarner Losh } 1109c8befdd5SWarner Losh 1110c8befdd5SWarner Losh /* Allocate interrupt */ 1111c8befdd5SWarner Losh rid = 0; 1112c8befdd5SWarner Losh sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 1113c8befdd5SWarner Losh RF_SHAREABLE | RF_ACTIVE); 1114c8befdd5SWarner Losh 1115c8befdd5SWarner Losh if (sc->ste_irq == NULL) { 1116c8befdd5SWarner Losh device_printf(dev, "couldn't map interrupt\n"); 1117c8befdd5SWarner Losh error = ENXIO; 1118c8befdd5SWarner Losh goto fail; 1119c8befdd5SWarner Losh } 1120c8befdd5SWarner Losh 112110f695eeSPyun YongHyeon callout_init_mtx(&sc->ste_callout, &sc->ste_mtx, 0); 1122c8befdd5SWarner Losh 1123c8befdd5SWarner Losh /* Reset the adapter. */ 1124c8befdd5SWarner Losh ste_reset(sc); 1125c8befdd5SWarner Losh 1126c8befdd5SWarner Losh /* 1127c8befdd5SWarner Losh * Get station address from the EEPROM. 1128c8befdd5SWarner Losh */ 1129fcd8385eSPyun YongHyeon if (ste_read_eeprom(sc, eaddr, STE_EEADDR_NODE0, ETHER_ADDR_LEN / 2)) { 1130c8befdd5SWarner Losh device_printf(dev, "failed to read station address\n"); 1131c2ede4b3SMartin Blapp error = ENXIO; 1132c8befdd5SWarner Losh goto fail; 1133c8befdd5SWarner Losh } 11348657caa6SPyun YongHyeon ste_sysctl_node(sc); 1135c8befdd5SWarner Losh 1136a1b2c209SPyun YongHyeon if ((error = ste_dma_alloc(sc)) != 0) 1137c8befdd5SWarner Losh goto fail; 1138c8befdd5SWarner Losh 1139c8befdd5SWarner Losh ifp = sc->ste_ifp = if_alloc(IFT_ETHER); 1140c8befdd5SWarner Losh if (ifp == NULL) { 1141c8befdd5SWarner Losh device_printf(dev, "can not if_alloc()\n"); 1142c8befdd5SWarner Losh error = ENOSPC; 1143c8befdd5SWarner Losh goto fail; 1144c8befdd5SWarner Losh } 1145c8befdd5SWarner Losh 1146c8befdd5SWarner Losh /* Do MII setup. */ 11478e5d93dbSMarius Strobl phy = MII_PHY_ANY; 11488e5d93dbSMarius Strobl if ((sc->ste_flags & STE_FLAG_ONE_PHY) != 0) 11498e5d93dbSMarius Strobl phy = 0; 11508e5d93dbSMarius Strobl error = mii_attach(dev, &sc->ste_miibus, ifp, ste_ifmedia_upd, 11518e5d93dbSMarius Strobl ste_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 11528e5d93dbSMarius Strobl if (error != 0) { 11538e5d93dbSMarius Strobl device_printf(dev, "attaching PHYs failed\n"); 1154c8befdd5SWarner Losh goto fail; 1155c8befdd5SWarner Losh } 1156c8befdd5SWarner Losh 1157c8befdd5SWarner Losh ifp->if_softc = sc; 1158c8befdd5SWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1159c8befdd5SWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1160c8befdd5SWarner Losh ifp->if_ioctl = ste_ioctl; 1161c8befdd5SWarner Losh ifp->if_start = ste_start; 1162c8befdd5SWarner Losh ifp->if_init = ste_init; 1163c8befdd5SWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, STE_TX_LIST_CNT - 1); 1164c8befdd5SWarner Losh ifp->if_snd.ifq_drv_maxlen = STE_TX_LIST_CNT - 1; 1165c8befdd5SWarner Losh IFQ_SET_READY(&ifp->if_snd); 1166c8befdd5SWarner Losh 1167c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1168c8befdd5SWarner Losh 1169c8befdd5SWarner Losh /* 1170c8befdd5SWarner Losh * Call MI attach routine. 1171c8befdd5SWarner Losh */ 1172fcd8385eSPyun YongHyeon ether_ifattach(ifp, (uint8_t *)eaddr); 1173c8befdd5SWarner Losh 1174c8befdd5SWarner Losh /* 1175c8befdd5SWarner Losh * Tell the upper layer(s) we support long frames. 1176c8befdd5SWarner Losh */ 1177c8befdd5SWarner Losh ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 1178c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 1179b4c170e1SPyun YongHyeon if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0) 1180b4c170e1SPyun YongHyeon ifp->if_capabilities |= IFCAP_WOL_MAGIC; 1181c8befdd5SWarner Losh ifp->if_capenable = ifp->if_capabilities; 1182c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1183c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1184c8befdd5SWarner Losh #endif 1185c8befdd5SWarner Losh 1186c8befdd5SWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1187c8befdd5SWarner Losh error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET | INTR_MPSAFE, 1188c8befdd5SWarner Losh NULL, ste_intr, sc, &sc->ste_intrhand); 1189c8befdd5SWarner Losh 1190c8befdd5SWarner Losh if (error) { 1191c8befdd5SWarner Losh device_printf(dev, "couldn't set up irq\n"); 1192c8befdd5SWarner Losh ether_ifdetach(ifp); 1193c8befdd5SWarner Losh goto fail; 1194c8befdd5SWarner Losh } 1195c8befdd5SWarner Losh 1196c8befdd5SWarner Losh fail: 1197c8befdd5SWarner Losh if (error) 1198c8befdd5SWarner Losh ste_detach(dev); 1199c8befdd5SWarner Losh 1200c8befdd5SWarner Losh return (error); 1201c8befdd5SWarner Losh } 1202c8befdd5SWarner Losh 1203c8befdd5SWarner Losh /* 1204c8befdd5SWarner Losh * Shutdown hardware and free up resources. This can be called any 1205c8befdd5SWarner Losh * time after the mutex has been initialized. It is called in both 1206c8befdd5SWarner Losh * the error case in attach and the normal detach case so it needs 1207c8befdd5SWarner Losh * to be careful about only freeing resources that have actually been 1208c8befdd5SWarner Losh * allocated. 1209c8befdd5SWarner Losh */ 1210c8befdd5SWarner Losh static int 121160270842SPyun YongHyeon ste_detach(device_t dev) 1212c8befdd5SWarner Losh { 1213c8befdd5SWarner Losh struct ste_softc *sc; 1214c8befdd5SWarner Losh struct ifnet *ifp; 1215c8befdd5SWarner Losh 1216c8befdd5SWarner Losh sc = device_get_softc(dev); 1217c8befdd5SWarner Losh KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized")); 1218c8befdd5SWarner Losh ifp = sc->ste_ifp; 1219c8befdd5SWarner Losh 1220c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1221c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1222c8befdd5SWarner Losh ether_poll_deregister(ifp); 1223c8befdd5SWarner Losh #endif 1224c8befdd5SWarner Losh 1225c8befdd5SWarner Losh /* These should only be active if attach succeeded */ 1226c8befdd5SWarner Losh if (device_is_attached(dev)) { 12277cf545d0SJohn Baldwin ether_ifdetach(ifp); 1228c8befdd5SWarner Losh STE_LOCK(sc); 1229c8befdd5SWarner Losh ste_stop(sc); 1230c8befdd5SWarner Losh STE_UNLOCK(sc); 123110f695eeSPyun YongHyeon callout_drain(&sc->ste_callout); 1232c8befdd5SWarner Losh } 1233c8befdd5SWarner Losh if (sc->ste_miibus) 1234c8befdd5SWarner Losh device_delete_child(dev, sc->ste_miibus); 1235c8befdd5SWarner Losh bus_generic_detach(dev); 1236c8befdd5SWarner Losh 1237c8befdd5SWarner Losh if (sc->ste_intrhand) 1238c8befdd5SWarner Losh bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); 1239c8befdd5SWarner Losh if (sc->ste_irq) 1240c8befdd5SWarner Losh bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); 1241c8befdd5SWarner Losh if (sc->ste_res) 1242c0270e60SPyun YongHyeon bus_release_resource(dev, sc->ste_res_type, sc->ste_res_id, 1243c0270e60SPyun YongHyeon sc->ste_res); 1244c8befdd5SWarner Losh 1245c8befdd5SWarner Losh if (ifp) 1246c8befdd5SWarner Losh if_free(ifp); 1247c8befdd5SWarner Losh 1248a1b2c209SPyun YongHyeon ste_dma_free(sc); 1249c8befdd5SWarner Losh mtx_destroy(&sc->ste_mtx); 1250c8befdd5SWarner Losh 1251c8befdd5SWarner Losh return (0); 1252c8befdd5SWarner Losh } 1253c8befdd5SWarner Losh 1254a1b2c209SPyun YongHyeon struct ste_dmamap_arg { 1255a1b2c209SPyun YongHyeon bus_addr_t ste_busaddr; 1256a1b2c209SPyun YongHyeon }; 1257a1b2c209SPyun YongHyeon 1258a1b2c209SPyun YongHyeon static void 1259a1b2c209SPyun YongHyeon ste_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1260c8befdd5SWarner Losh { 1261a1b2c209SPyun YongHyeon struct ste_dmamap_arg *ctx; 1262c8befdd5SWarner Losh 1263a1b2c209SPyun YongHyeon if (error != 0) 1264a1b2c209SPyun YongHyeon return; 1265a1b2c209SPyun YongHyeon 1266a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1267a1b2c209SPyun YongHyeon 1268a1b2c209SPyun YongHyeon ctx = (struct ste_dmamap_arg *)arg; 1269a1b2c209SPyun YongHyeon ctx->ste_busaddr = segs[0].ds_addr; 1270c8befdd5SWarner Losh } 1271c8befdd5SWarner Losh 1272a1b2c209SPyun YongHyeon static int 1273a1b2c209SPyun YongHyeon ste_dma_alloc(struct ste_softc *sc) 1274a1b2c209SPyun YongHyeon { 1275a1b2c209SPyun YongHyeon struct ste_chain *txc; 1276a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1277a1b2c209SPyun YongHyeon struct ste_dmamap_arg ctx; 1278a1b2c209SPyun YongHyeon int error, i; 1279c8befdd5SWarner Losh 1280a1b2c209SPyun YongHyeon /* Create parent DMA tag. */ 1281a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1282a1b2c209SPyun YongHyeon bus_get_dma_tag(sc->ste_dev), /* parent */ 1283a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1284a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1285a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1286a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1287a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 1288a1b2c209SPyun YongHyeon 0, /* nsegments */ 1289a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1290a1b2c209SPyun YongHyeon 0, /* flags */ 1291a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1292a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_parent_tag); 1293a1b2c209SPyun YongHyeon if (error != 0) { 1294a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1295a1b2c209SPyun YongHyeon "could not create parent DMA tag.\n"); 1296a1b2c209SPyun YongHyeon goto fail; 1297a1b2c209SPyun YongHyeon } 1298c8befdd5SWarner Losh 1299a1b2c209SPyun YongHyeon /* Create DMA tag for Tx descriptor list. */ 1300a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1301a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1302a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1303a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1304a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1305a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1306a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsize */ 1307a1b2c209SPyun YongHyeon 1, /* nsegments */ 1308a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsegsize */ 1309a1b2c209SPyun YongHyeon 0, /* flags */ 1310a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1311a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_tag); 1312a1b2c209SPyun YongHyeon if (error != 0) { 1313a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1314a1b2c209SPyun YongHyeon "could not create Tx list DMA tag.\n"); 1315a1b2c209SPyun YongHyeon goto fail; 1316a1b2c209SPyun YongHyeon } 1317a1b2c209SPyun YongHyeon 1318a1b2c209SPyun YongHyeon /* Create DMA tag for Rx descriptor list. */ 1319a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1320a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1321a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1322a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1323a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1324a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1325a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsize */ 1326a1b2c209SPyun YongHyeon 1, /* nsegments */ 1327a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsegsize */ 1328a1b2c209SPyun YongHyeon 0, /* flags */ 1329a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1330a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_tag); 1331a1b2c209SPyun YongHyeon if (error != 0) { 1332a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1333a1b2c209SPyun YongHyeon "could not create Rx list DMA tag.\n"); 1334a1b2c209SPyun YongHyeon goto fail; 1335a1b2c209SPyun YongHyeon } 1336a1b2c209SPyun YongHyeon 1337a1b2c209SPyun YongHyeon /* Create DMA tag for Tx buffers. */ 1338a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1339a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1340a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1341a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1342a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1343a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1344a1b2c209SPyun YongHyeon MCLBYTES * STE_MAXFRAGS, /* maxsize */ 1345a1b2c209SPyun YongHyeon STE_MAXFRAGS, /* nsegments */ 1346a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1347a1b2c209SPyun YongHyeon 0, /* flags */ 1348a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1349a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_tag); 1350a1b2c209SPyun YongHyeon if (error != 0) { 1351a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Tx DMA tag.\n"); 1352a1b2c209SPyun YongHyeon goto fail; 1353a1b2c209SPyun YongHyeon } 1354a1b2c209SPyun YongHyeon 1355a1b2c209SPyun YongHyeon /* Create DMA tag for Rx buffers. */ 1356a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1357a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1358a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1359a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1360a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1361a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1362a1b2c209SPyun YongHyeon MCLBYTES, /* maxsize */ 1363a1b2c209SPyun YongHyeon 1, /* nsegments */ 1364a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1365a1b2c209SPyun YongHyeon 0, /* flags */ 1366a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1367a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_tag); 1368a1b2c209SPyun YongHyeon if (error != 0) { 1369a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Rx DMA tag.\n"); 1370a1b2c209SPyun YongHyeon goto fail; 1371a1b2c209SPyun YongHyeon } 1372a1b2c209SPyun YongHyeon 1373a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Tx list. */ 1374a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_tx_list_tag, 1375a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_tx_list, 1376a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1377a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_map); 1378a1b2c209SPyun YongHyeon if (error != 0) { 1379a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1380a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Tx list.\n"); 1381a1b2c209SPyun YongHyeon goto fail; 1382a1b2c209SPyun YongHyeon } 1383a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1384a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_tx_list_tag, 1385a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, sc->ste_ldata.ste_tx_list, 1386a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1387a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1388a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1389a1b2c209SPyun YongHyeon "could not load DMA'able memory for Tx list.\n"); 1390a1b2c209SPyun YongHyeon goto fail; 1391a1b2c209SPyun YongHyeon } 1392a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list_paddr = ctx.ste_busaddr; 1393a1b2c209SPyun YongHyeon 1394a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Rx list. */ 1395a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_rx_list_tag, 1396a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_rx_list, 1397a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1398a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_map); 1399a1b2c209SPyun YongHyeon if (error != 0) { 1400a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1401a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Rx list.\n"); 1402a1b2c209SPyun YongHyeon goto fail; 1403a1b2c209SPyun YongHyeon } 1404a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1405a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_rx_list_tag, 1406a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, sc->ste_ldata.ste_rx_list, 1407a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1408a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1409a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1410a1b2c209SPyun YongHyeon "could not load DMA'able memory for Rx list.\n"); 1411a1b2c209SPyun YongHyeon goto fail; 1412a1b2c209SPyun YongHyeon } 1413a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list_paddr = ctx.ste_busaddr; 1414a1b2c209SPyun YongHyeon 1415a1b2c209SPyun YongHyeon /* Create DMA maps for Tx buffers. */ 1416a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1417a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1418a1b2c209SPyun YongHyeon txc->ste_ptr = NULL; 1419a1b2c209SPyun YongHyeon txc->ste_mbuf = NULL; 1420a1b2c209SPyun YongHyeon txc->ste_next = NULL; 1421a1b2c209SPyun YongHyeon txc->ste_phys = 0; 1422a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1423a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_tx_tag, 0, 1424a1b2c209SPyun YongHyeon &txc->ste_map); 1425a1b2c209SPyun YongHyeon if (error != 0) { 1426a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1427a1b2c209SPyun YongHyeon "could not create Tx dmamap.\n"); 1428a1b2c209SPyun YongHyeon goto fail; 1429a1b2c209SPyun YongHyeon } 1430a1b2c209SPyun YongHyeon } 1431a1b2c209SPyun YongHyeon /* Create DMA maps for Rx buffers. */ 1432a1b2c209SPyun YongHyeon if ((error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1433a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_sparemap)) != 0) { 1434a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1435a1b2c209SPyun YongHyeon "could not create spare Rx dmamap.\n"); 1436a1b2c209SPyun YongHyeon goto fail; 1437a1b2c209SPyun YongHyeon } 1438a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1439a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1440a1b2c209SPyun YongHyeon rxc->ste_ptr = NULL; 1441a1b2c209SPyun YongHyeon rxc->ste_mbuf = NULL; 1442a1b2c209SPyun YongHyeon rxc->ste_next = NULL; 1443a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1444a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1445a1b2c209SPyun YongHyeon &rxc->ste_map); 1446a1b2c209SPyun YongHyeon if (error != 0) { 1447a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1448a1b2c209SPyun YongHyeon "could not create Rx dmamap.\n"); 1449a1b2c209SPyun YongHyeon goto fail; 1450a1b2c209SPyun YongHyeon } 1451a1b2c209SPyun YongHyeon } 1452a1b2c209SPyun YongHyeon 1453a1b2c209SPyun YongHyeon fail: 1454a1b2c209SPyun YongHyeon return (error); 1455a1b2c209SPyun YongHyeon } 1456a1b2c209SPyun YongHyeon 1457a1b2c209SPyun YongHyeon static void 1458a1b2c209SPyun YongHyeon ste_dma_free(struct ste_softc *sc) 1459a1b2c209SPyun YongHyeon { 1460a1b2c209SPyun YongHyeon struct ste_chain *txc; 1461a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1462a1b2c209SPyun YongHyeon int i; 1463a1b2c209SPyun YongHyeon 1464a1b2c209SPyun YongHyeon /* Tx buffers. */ 1465a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_tag != NULL) { 1466a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1467a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1468a1b2c209SPyun YongHyeon if (txc->ste_map != NULL) { 1469a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_tx_tag, 1470a1b2c209SPyun YongHyeon txc->ste_map); 1471a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1472a1b2c209SPyun YongHyeon } 1473a1b2c209SPyun YongHyeon } 1474a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_tag); 1475a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_tag = NULL; 1476a1b2c209SPyun YongHyeon } 1477a1b2c209SPyun YongHyeon /* Rx buffers. */ 1478a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_tag != NULL) { 1479a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1480a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1481a1b2c209SPyun YongHyeon if (rxc->ste_map != NULL) { 1482a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1483a1b2c209SPyun YongHyeon rxc->ste_map); 1484a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1485a1b2c209SPyun YongHyeon } 1486a1b2c209SPyun YongHyeon } 1487a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_sparemap != NULL) { 1488a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1489a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap); 1490a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = NULL; 1491a1b2c209SPyun YongHyeon } 1492a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_tag); 1493a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_tag = NULL; 1494a1b2c209SPyun YongHyeon } 1495a1b2c209SPyun YongHyeon /* Tx descriptor list. */ 1496a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_tag != NULL) { 1497a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_map != NULL) 1498a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_list_tag, 1499a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1500a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_map != NULL && 1501a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list != NULL) 1502a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_tx_list_tag, 1503a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list, 1504a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1505a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list = NULL; 1506a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map = NULL; 1507a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_list_tag); 1508a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_tag = NULL; 1509a1b2c209SPyun YongHyeon } 1510a1b2c209SPyun YongHyeon /* Rx descriptor list. */ 1511a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_tag != NULL) { 1512a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_map != NULL) 1513a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_list_tag, 1514a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1515a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_map != NULL && 1516a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list != NULL) 1517a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_rx_list_tag, 1518a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list, 1519a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1520a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list = NULL; 1521a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map = NULL; 1522a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_list_tag); 1523a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_tag = NULL; 1524a1b2c209SPyun YongHyeon } 1525a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_parent_tag != NULL) { 1526a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_parent_tag); 1527a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag = NULL; 1528a1b2c209SPyun YongHyeon } 1529a1b2c209SPyun YongHyeon } 1530a1b2c209SPyun YongHyeon 1531a1b2c209SPyun YongHyeon static int 1532a1b2c209SPyun YongHyeon ste_newbuf(struct ste_softc *sc, struct ste_chain_onefrag *rxc) 1533a1b2c209SPyun YongHyeon { 1534a1b2c209SPyun YongHyeon struct mbuf *m; 1535a1b2c209SPyun YongHyeon bus_dma_segment_t segs[1]; 1536a1b2c209SPyun YongHyeon bus_dmamap_t map; 1537a1b2c209SPyun YongHyeon int error, nsegs; 1538a1b2c209SPyun YongHyeon 1539a1b2c209SPyun YongHyeon m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1540a1b2c209SPyun YongHyeon if (m == NULL) 1541a1b2c209SPyun YongHyeon return (ENOBUFS); 1542a1b2c209SPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES; 1543a1b2c209SPyun YongHyeon m_adj(m, ETHER_ALIGN); 1544a1b2c209SPyun YongHyeon 1545a1b2c209SPyun YongHyeon if ((error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_rx_tag, 1546a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap, m, segs, &nsegs, 0)) != 0) { 1547a1b2c209SPyun YongHyeon m_freem(m); 1548a1b2c209SPyun YongHyeon return (error); 1549a1b2c209SPyun YongHyeon } 1550a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1551a1b2c209SPyun YongHyeon 1552a1b2c209SPyun YongHyeon if (rxc->ste_mbuf != NULL) { 1553a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1554a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD); 1555a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, rxc->ste_map); 1556a1b2c209SPyun YongHyeon } 1557a1b2c209SPyun YongHyeon map = rxc->ste_map; 1558a1b2c209SPyun YongHyeon rxc->ste_map = sc->ste_cdata.ste_rx_sparemap; 1559a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = map; 1560a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1561a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD); 1562a1b2c209SPyun YongHyeon rxc->ste_mbuf = m; 1563a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_status = 0; 1564a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_addr = htole32(segs[0].ds_addr); 1565a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_len = htole32(segs[0].ds_len | 1566a1b2c209SPyun YongHyeon STE_FRAG_LAST); 1567c8befdd5SWarner Losh return (0); 1568c8befdd5SWarner Losh } 1569c8befdd5SWarner Losh 1570c8befdd5SWarner Losh static int 157160270842SPyun YongHyeon ste_init_rx_list(struct ste_softc *sc) 1572c8befdd5SWarner Losh { 1573c8befdd5SWarner Losh struct ste_chain_data *cd; 1574c8befdd5SWarner Losh struct ste_list_data *ld; 1575a1b2c209SPyun YongHyeon int error, i; 1576c8befdd5SWarner Losh 1577fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0; 1578c8befdd5SWarner Losh cd = &sc->ste_cdata; 1579a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1580a1b2c209SPyun YongHyeon bzero(ld->ste_rx_list, STE_RX_LIST_SZ); 1581c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1582c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; 1583a1b2c209SPyun YongHyeon error = ste_newbuf(sc, &cd->ste_rx_chain[i]); 1584a1b2c209SPyun YongHyeon if (error != 0) 1585a1b2c209SPyun YongHyeon return (error); 1586c8befdd5SWarner Losh if (i == (STE_RX_LIST_CNT - 1)) { 1587a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[0]; 1588e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next = 1589e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr + 1590e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * 0)); 1591c8befdd5SWarner Losh } else { 1592a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[i + 1]; 1593e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next = 1594e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr + 1595e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * (i + 1))); 1596c8befdd5SWarner Losh } 1597c8befdd5SWarner Losh } 1598c8befdd5SWarner Losh 1599c8befdd5SWarner Losh cd->ste_rx_head = &cd->ste_rx_chain[0]; 1600a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 1601a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 1602a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1603c8befdd5SWarner Losh 1604c8befdd5SWarner Losh return (0); 1605c8befdd5SWarner Losh } 1606c8befdd5SWarner Losh 1607c8befdd5SWarner Losh static void 160860270842SPyun YongHyeon ste_init_tx_list(struct ste_softc *sc) 1609c8befdd5SWarner Losh { 1610c8befdd5SWarner Losh struct ste_chain_data *cd; 1611c8befdd5SWarner Losh struct ste_list_data *ld; 1612c8befdd5SWarner Losh int i; 1613c8befdd5SWarner Losh 1614c8befdd5SWarner Losh cd = &sc->ste_cdata; 1615a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1616a1b2c209SPyun YongHyeon bzero(ld->ste_tx_list, STE_TX_LIST_SZ); 1617c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1618c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; 1619a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_mbuf = NULL; 1620a1b2c209SPyun YongHyeon if (i == (STE_TX_LIST_CNT - 1)) { 1621a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[0]; 1622a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1623a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1624a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * 0))); 1625a1b2c209SPyun YongHyeon } else { 1626a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1]; 1627a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1628a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1629a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * (i + 1)))); 1630a1b2c209SPyun YongHyeon } 1631c8befdd5SWarner Losh } 1632c8befdd5SWarner Losh 1633a1b2c209SPyun YongHyeon cd->ste_last_tx = NULL; 1634c8befdd5SWarner Losh cd->ste_tx_prod = 0; 1635c8befdd5SWarner Losh cd->ste_tx_cons = 0; 1636a1b2c209SPyun YongHyeon cd->ste_tx_cnt = 0; 1637a1b2c209SPyun YongHyeon 1638a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1639a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1640a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1641c8befdd5SWarner Losh } 1642c8befdd5SWarner Losh 1643c8befdd5SWarner Losh static void 164460270842SPyun YongHyeon ste_init(void *xsc) 1645c8befdd5SWarner Losh { 1646c8befdd5SWarner Losh struct ste_softc *sc; 1647c8befdd5SWarner Losh 1648c8befdd5SWarner Losh sc = xsc; 1649c8befdd5SWarner Losh STE_LOCK(sc); 1650c8befdd5SWarner Losh ste_init_locked(sc); 1651c8befdd5SWarner Losh STE_UNLOCK(sc); 1652c8befdd5SWarner Losh } 1653c8befdd5SWarner Losh 1654c8befdd5SWarner Losh static void 165560270842SPyun YongHyeon ste_init_locked(struct ste_softc *sc) 1656c8befdd5SWarner Losh { 1657c8befdd5SWarner Losh struct ifnet *ifp; 1658bfe051bdSPyun YongHyeon struct mii_data *mii; 1659b4c170e1SPyun YongHyeon uint8_t val; 1660f2632c3bSPyun YongHyeon int i; 1661c8befdd5SWarner Losh 1662c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1663c8befdd5SWarner Losh ifp = sc->ste_ifp; 1664bfe051bdSPyun YongHyeon mii = device_get_softc(sc->ste_miibus); 1665c8befdd5SWarner Losh 166655d7003eSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 166755d7003eSPyun YongHyeon return; 166855d7003eSPyun YongHyeon 1669c8befdd5SWarner Losh ste_stop(sc); 16708d9f6dd9SPyun YongHyeon /* Reset the chip to a known state. */ 16718d9f6dd9SPyun YongHyeon ste_reset(sc); 1672c8befdd5SWarner Losh 1673c8befdd5SWarner Losh /* Init our MAC address */ 1674c8befdd5SWarner Losh for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 1675c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PAR0 + i, 1676c8befdd5SWarner Losh ((IF_LLADDR(sc->ste_ifp)[i] & 0xff) | 1677c8befdd5SWarner Losh IF_LLADDR(sc->ste_ifp)[i + 1] << 8)); 1678c8befdd5SWarner Losh } 1679c8befdd5SWarner Losh 1680c8befdd5SWarner Losh /* Init RX list */ 1681a1b2c209SPyun YongHyeon if (ste_init_rx_list(sc) != 0) { 1682c8befdd5SWarner Losh device_printf(sc->ste_dev, 1683c8befdd5SWarner Losh "initialization failed: no memory for RX buffers\n"); 1684c8befdd5SWarner Losh ste_stop(sc); 1685c8befdd5SWarner Losh return; 1686c8befdd5SWarner Losh } 1687c8befdd5SWarner Losh 1688c8befdd5SWarner Losh /* Set RX polling interval */ 1689c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); 1690c8befdd5SWarner Losh 1691c8befdd5SWarner Losh /* Init TX descriptors */ 1692c8befdd5SWarner Losh ste_init_tx_list(sc); 1693c8befdd5SWarner Losh 1694b4c170e1SPyun YongHyeon /* Clear and disable WOL. */ 1695b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT); 1696b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB | 1697b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB); 1698b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val); 1699b4c170e1SPyun YongHyeon 1700c8befdd5SWarner Losh /* Set the TX freethresh value */ 1701c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); 1702c8befdd5SWarner Losh 1703c8befdd5SWarner Losh /* Set the TX start threshold for best performance. */ 1704c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 1705c8befdd5SWarner Losh 1706c8befdd5SWarner Losh /* Set the TX reclaim threshold. */ 1707c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); 1708c8befdd5SWarner Losh 1709931ec15aSPyun YongHyeon /* Accept VLAN length packets */ 1710931ec15aSPyun YongHyeon CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN); 1711931ec15aSPyun YongHyeon 1712c8befdd5SWarner Losh /* Set up the RX filter. */ 1713931ec15aSPyun YongHyeon ste_rxfilter(sc); 1714c8befdd5SWarner Losh 1715c8befdd5SWarner Losh /* Load the address of the RX list. */ 1716c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1717c8befdd5SWarner Losh ste_wait(sc); 1718c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 1719a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_rx_list_paddr)); 1720c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1721c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1722c8befdd5SWarner Losh 1723a1b2c209SPyun YongHyeon /* Set TX polling interval(defer until we TX first packet). */ 1724c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 1725c8befdd5SWarner Losh 1726c8befdd5SWarner Losh /* Load address of the TX list */ 1727c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1728c8befdd5SWarner Losh ste_wait(sc); 1729c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 1730c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1731c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1732c8befdd5SWarner Losh ste_wait(sc); 1733fabbaac5SPyun YongHyeon /* Select 3.2us timer. */ 1734fabbaac5SPyun YongHyeon STE_CLRBIT4(sc, STE_DMACTL, STE_DMACTL_COUNTDOWN_SPEED | 1735fabbaac5SPyun YongHyeon STE_DMACTL_COUNTDOWN_MODE); 1736c8befdd5SWarner Losh 1737c8befdd5SWarner Losh /* Enable receiver and transmitter */ 1738c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL0, 0); 1739c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL1, 0); 1740c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); 1741c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); 1742c8befdd5SWarner Losh 1743c8befdd5SWarner Losh /* Enable stats counters. */ 1744c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); 17458657caa6SPyun YongHyeon /* Clear stats counters. */ 17468657caa6SPyun YongHyeon ste_stats_clear(sc); 1747c8befdd5SWarner Losh 1748fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0); 1749c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_ISR, 0xFFFF); 1750c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1751c8befdd5SWarner Losh /* Disable interrupts if we are polling. */ 1752c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1753c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1754c8befdd5SWarner Losh else 1755c8befdd5SWarner Losh #endif 1756c8befdd5SWarner Losh /* Enable interrupts. */ 1757c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1758c8befdd5SWarner Losh 1759bfe051bdSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK; 1760bfe051bdSPyun YongHyeon /* Switch to the current media. */ 1761bfe051bdSPyun YongHyeon mii_mediachg(mii); 1762c8befdd5SWarner Losh 1763c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 1764c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1765c8befdd5SWarner Losh 176610f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc); 1767c8befdd5SWarner Losh } 1768c8befdd5SWarner Losh 1769c8befdd5SWarner Losh static void 177060270842SPyun YongHyeon ste_stop(struct ste_softc *sc) 1771c8befdd5SWarner Losh { 1772c8befdd5SWarner Losh struct ifnet *ifp; 1773a1b2c209SPyun YongHyeon struct ste_chain_onefrag *cur_rx; 1774a1b2c209SPyun YongHyeon struct ste_chain *cur_tx; 17758d9f6dd9SPyun YongHyeon uint32_t val; 1776f2632c3bSPyun YongHyeon int i; 1777c8befdd5SWarner Losh 1778c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1779c8befdd5SWarner Losh ifp = sc->ste_ifp; 1780c8befdd5SWarner Losh 178110f695eeSPyun YongHyeon callout_stop(&sc->ste_callout); 178210f695eeSPyun YongHyeon sc->ste_timer = 0; 1783c8befdd5SWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE); 1784c8befdd5SWarner Losh 1785c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1786fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0); 17878d9f6dd9SPyun YongHyeon /* Stop pending DMA. */ 17888d9f6dd9SPyun YongHyeon val = CSR_READ_4(sc, STE_DMACTL); 17898d9f6dd9SPyun YongHyeon val |= STE_DMACTL_TXDMA_STALL | STE_DMACTL_RXDMA_STALL; 17908d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_DMACTL, val); 1791c8befdd5SWarner Losh ste_wait(sc); 17928d9f6dd9SPyun YongHyeon /* Disable auto-polling. */ 17938d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 0); 17948d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 17958d9f6dd9SPyun YongHyeon /* Nullify DMA address to stop any further DMA. */ 17968d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 0); 17978d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 17988d9f6dd9SPyun YongHyeon /* Stop TX/RX MAC. */ 17998d9f6dd9SPyun YongHyeon val = CSR_READ_2(sc, STE_MACCTL1); 18008d9f6dd9SPyun YongHyeon val |= STE_MACCTL1_TX_DISABLE | STE_MACCTL1_RX_DISABLE | 18018d9f6dd9SPyun YongHyeon STE_MACCTL1_STATS_DISABLE; 18028d9f6dd9SPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, val); 18038d9f6dd9SPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) { 18048d9f6dd9SPyun YongHyeon DELAY(10); 18058d9f6dd9SPyun YongHyeon if ((CSR_READ_2(sc, STE_MACCTL1) & (STE_MACCTL1_TX_DISABLE | 18068d9f6dd9SPyun YongHyeon STE_MACCTL1_RX_DISABLE | STE_MACCTL1_STATS_DISABLE)) == 0) 18078d9f6dd9SPyun YongHyeon break; 18088d9f6dd9SPyun YongHyeon } 18098d9f6dd9SPyun YongHyeon if (i == STE_TIMEOUT) 18108d9f6dd9SPyun YongHyeon device_printf(sc->ste_dev, "Stopping MAC timed out\n"); 18118d9f6dd9SPyun YongHyeon /* Acknowledge any pending interrupts. */ 18128d9f6dd9SPyun YongHyeon CSR_READ_2(sc, STE_ISR_ACK); 18138d9f6dd9SPyun YongHyeon ste_stats_update(sc); 1814c8befdd5SWarner Losh 1815c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1816a1b2c209SPyun YongHyeon cur_rx = &sc->ste_cdata.ste_rx_chain[i]; 1817a1b2c209SPyun YongHyeon if (cur_rx->ste_mbuf != NULL) { 1818a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, 1819a1b2c209SPyun YongHyeon cur_rx->ste_map, BUS_DMASYNC_POSTREAD); 1820a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, 1821a1b2c209SPyun YongHyeon cur_rx->ste_map); 1822a1b2c209SPyun YongHyeon m_freem(cur_rx->ste_mbuf); 1823a1b2c209SPyun YongHyeon cur_rx->ste_mbuf = NULL; 1824c8befdd5SWarner Losh } 1825c8befdd5SWarner Losh } 1826c8befdd5SWarner Losh 1827c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1828a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[i]; 1829a1b2c209SPyun YongHyeon if (cur_tx->ste_mbuf != NULL) { 1830a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, 1831a1b2c209SPyun YongHyeon cur_tx->ste_map, BUS_DMASYNC_POSTWRITE); 1832a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, 1833a1b2c209SPyun YongHyeon cur_tx->ste_map); 1834a1b2c209SPyun YongHyeon m_freem(cur_tx->ste_mbuf); 1835a1b2c209SPyun YongHyeon cur_tx->ste_mbuf = NULL; 1836c8befdd5SWarner Losh } 1837c8befdd5SWarner Losh } 1838c8befdd5SWarner Losh } 1839c8befdd5SWarner Losh 1840c8befdd5SWarner Losh static void 184160270842SPyun YongHyeon ste_reset(struct ste_softc *sc) 1842c8befdd5SWarner Losh { 184338c52cfdSPyun YongHyeon uint32_t ctl; 1844c8befdd5SWarner Losh int i; 1845c8befdd5SWarner Losh 184638c52cfdSPyun YongHyeon ctl = CSR_READ_4(sc, STE_ASICCTL); 184738c52cfdSPyun YongHyeon ctl |= STE_ASICCTL_GLOBAL_RESET | STE_ASICCTL_RX_RESET | 1848c8befdd5SWarner Losh STE_ASICCTL_TX_RESET | STE_ASICCTL_DMA_RESET | 1849c8befdd5SWarner Losh STE_ASICCTL_FIFO_RESET | STE_ASICCTL_NETWORK_RESET | 1850c8befdd5SWarner Losh STE_ASICCTL_AUTOINIT_RESET |STE_ASICCTL_HOST_RESET | 185138c52cfdSPyun YongHyeon STE_ASICCTL_EXTRESET_RESET; 185238c52cfdSPyun YongHyeon CSR_WRITE_4(sc, STE_ASICCTL, ctl); 185338c52cfdSPyun YongHyeon CSR_READ_4(sc, STE_ASICCTL); 185438c52cfdSPyun YongHyeon /* 185538c52cfdSPyun YongHyeon * Due to the need of accessing EEPROM controller can take 185638c52cfdSPyun YongHyeon * up to 1ms to complete the global reset. 185738c52cfdSPyun YongHyeon */ 185838c52cfdSPyun YongHyeon DELAY(1000); 1859c8befdd5SWarner Losh 1860c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 1861c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) 1862c8befdd5SWarner Losh break; 186338c52cfdSPyun YongHyeon DELAY(10); 1864c8befdd5SWarner Losh } 1865c8befdd5SWarner Losh 1866c8befdd5SWarner Losh if (i == STE_TIMEOUT) 1867c8befdd5SWarner Losh device_printf(sc->ste_dev, "global reset never completed\n"); 1868c8befdd5SWarner Losh } 1869c8befdd5SWarner Losh 187081598b3eSPyun YongHyeon static void 187181598b3eSPyun YongHyeon ste_restart_tx(struct ste_softc *sc) 187281598b3eSPyun YongHyeon { 187381598b3eSPyun YongHyeon uint16_t mac; 187481598b3eSPyun YongHyeon int i; 187581598b3eSPyun YongHyeon 187681598b3eSPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) { 187781598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1); 187881598b3eSPyun YongHyeon mac |= STE_MACCTL1_TX_ENABLE; 187981598b3eSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, mac); 188081598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1); 188181598b3eSPyun YongHyeon if ((mac & STE_MACCTL1_TX_ENABLED) != 0) 188281598b3eSPyun YongHyeon break; 188381598b3eSPyun YongHyeon DELAY(10); 188481598b3eSPyun YongHyeon } 188581598b3eSPyun YongHyeon 188681598b3eSPyun YongHyeon if (i == STE_TIMEOUT) 188781598b3eSPyun YongHyeon device_printf(sc->ste_dev, "starting Tx failed"); 188881598b3eSPyun YongHyeon } 188981598b3eSPyun YongHyeon 1890c8befdd5SWarner Losh static int 189160270842SPyun YongHyeon ste_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 1892c8befdd5SWarner Losh { 1893c8befdd5SWarner Losh struct ste_softc *sc; 1894c8befdd5SWarner Losh struct ifreq *ifr; 1895c8befdd5SWarner Losh struct mii_data *mii; 1896b4c170e1SPyun YongHyeon int error = 0, mask; 1897c8befdd5SWarner Losh 1898c8befdd5SWarner Losh sc = ifp->if_softc; 1899c8befdd5SWarner Losh ifr = (struct ifreq *)data; 1900c8befdd5SWarner Losh 1901c8befdd5SWarner Losh switch (command) { 1902c8befdd5SWarner Losh case SIOCSIFFLAGS: 1903c8befdd5SWarner Losh STE_LOCK(sc); 1904931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 1905931ec15aSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 1906931ec15aSPyun YongHyeon ((ifp->if_flags ^ sc->ste_if_flags) & 1907931ec15aSPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0) 1908931ec15aSPyun YongHyeon ste_rxfilter(sc); 1909931ec15aSPyun YongHyeon else 1910c8befdd5SWarner Losh ste_init_locked(sc); 1911931ec15aSPyun YongHyeon } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1912c8befdd5SWarner Losh ste_stop(sc); 1913c8befdd5SWarner Losh sc->ste_if_flags = ifp->if_flags; 1914c8befdd5SWarner Losh STE_UNLOCK(sc); 1915c8befdd5SWarner Losh break; 1916c8befdd5SWarner Losh case SIOCADDMULTI: 1917c8befdd5SWarner Losh case SIOCDELMULTI: 1918c8befdd5SWarner Losh STE_LOCK(sc); 1919931ec15aSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1920931ec15aSPyun YongHyeon ste_rxfilter(sc); 1921c8befdd5SWarner Losh STE_UNLOCK(sc); 1922c8befdd5SWarner Losh break; 1923c8befdd5SWarner Losh case SIOCGIFMEDIA: 1924c8befdd5SWarner Losh case SIOCSIFMEDIA: 1925c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 1926c8befdd5SWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1927c8befdd5SWarner Losh break; 1928c8befdd5SWarner Losh case SIOCSIFCAP: 1929c8befdd5SWarner Losh STE_LOCK(sc); 1930b4c170e1SPyun YongHyeon mask = ifr->ifr_reqcap ^ ifp->if_capenable; 1931b4c170e1SPyun YongHyeon #ifdef DEVICE_POLLING 1932b4c170e1SPyun YongHyeon if ((mask & IFCAP_POLLING) != 0 && 1933b4c170e1SPyun YongHyeon (IFCAP_POLLING & ifp->if_capabilities) != 0) { 1934b4c170e1SPyun YongHyeon ifp->if_capenable ^= IFCAP_POLLING; 1935b4c170e1SPyun YongHyeon if ((IFCAP_POLLING & ifp->if_capenable) != 0) { 1936b4c170e1SPyun YongHyeon error = ether_poll_register(ste_poll, ifp); 1937b4c170e1SPyun YongHyeon if (error != 0) { 1938c8befdd5SWarner Losh STE_UNLOCK(sc); 1939b4c170e1SPyun YongHyeon break; 1940c8befdd5SWarner Losh } 1941b4c170e1SPyun YongHyeon /* Disable interrupts. */ 1942b4c170e1SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, 0); 1943b4c170e1SPyun YongHyeon } else { 1944c8befdd5SWarner Losh error = ether_poll_deregister(ifp); 1945c8befdd5SWarner Losh /* Enable interrupts. */ 1946c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1947b4c170e1SPyun YongHyeon } 1948c8befdd5SWarner Losh } 1949c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 1950b4c170e1SPyun YongHyeon if ((mask & IFCAP_WOL_MAGIC) != 0 && 1951b4c170e1SPyun YongHyeon (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) 1952b4c170e1SPyun YongHyeon ifp->if_capenable ^= IFCAP_WOL_MAGIC; 1953b4c170e1SPyun YongHyeon STE_UNLOCK(sc); 1954c8befdd5SWarner Losh break; 1955c8befdd5SWarner Losh default: 1956c8befdd5SWarner Losh error = ether_ioctl(ifp, command, data); 1957c8befdd5SWarner Losh break; 1958c8befdd5SWarner Losh } 1959c8befdd5SWarner Losh 1960c8befdd5SWarner Losh return (error); 1961c8befdd5SWarner Losh } 1962c8befdd5SWarner Losh 1963c8befdd5SWarner Losh static int 1964a1b2c209SPyun YongHyeon ste_encap(struct ste_softc *sc, struct mbuf **m_head, struct ste_chain *txc) 1965c8befdd5SWarner Losh { 1966a1b2c209SPyun YongHyeon struct ste_frag *frag; 1967c8befdd5SWarner Losh struct mbuf *m; 1968a1b2c209SPyun YongHyeon struct ste_desc *desc; 1969a1b2c209SPyun YongHyeon bus_dma_segment_t txsegs[STE_MAXFRAGS]; 1970a1b2c209SPyun YongHyeon int error, i, nsegs; 1971c8befdd5SWarner Losh 1972a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 1973a1b2c209SPyun YongHyeon M_ASSERTPKTHDR((*m_head)); 1974c8befdd5SWarner Losh 1975a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1976a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1977a1b2c209SPyun YongHyeon if (error == EFBIG) { 1978a1b2c209SPyun YongHyeon m = m_collapse(*m_head, M_DONTWAIT, STE_MAXFRAGS); 1979a1b2c209SPyun YongHyeon if (m == NULL) { 1980a1b2c209SPyun YongHyeon m_freem(*m_head); 1981a1b2c209SPyun YongHyeon *m_head = NULL; 1982a1b2c209SPyun YongHyeon return (ENOMEM); 1983c8befdd5SWarner Losh } 1984a1b2c209SPyun YongHyeon *m_head = m; 1985a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1986a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1987a1b2c209SPyun YongHyeon if (error != 0) { 1988a1b2c209SPyun YongHyeon m_freem(*m_head); 1989a1b2c209SPyun YongHyeon *m_head = NULL; 1990a1b2c209SPyun YongHyeon return (error); 1991c8befdd5SWarner Losh } 1992a1b2c209SPyun YongHyeon } else if (error != 0) 1993a1b2c209SPyun YongHyeon return (error); 1994a1b2c209SPyun YongHyeon if (nsegs == 0) { 1995a1b2c209SPyun YongHyeon m_freem(*m_head); 1996a1b2c209SPyun YongHyeon *m_head = NULL; 1997a1b2c209SPyun YongHyeon return (EIO); 1998a1b2c209SPyun YongHyeon } 1999a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, txc->ste_map, 2000a1b2c209SPyun YongHyeon BUS_DMASYNC_PREWRITE); 2001c8befdd5SWarner Losh 2002a1b2c209SPyun YongHyeon desc = txc->ste_ptr; 2003a1b2c209SPyun YongHyeon for (i = 0; i < nsegs; i++) { 2004a1b2c209SPyun YongHyeon frag = &desc->ste_frags[i]; 2005a1b2c209SPyun YongHyeon frag->ste_addr = htole32(STE_ADDR_LO(txsegs[i].ds_addr)); 2006a1b2c209SPyun YongHyeon frag->ste_len = htole32(txsegs[i].ds_len); 2007a1b2c209SPyun YongHyeon } 2008a1b2c209SPyun YongHyeon desc->ste_frags[i - 1].ste_len |= htole32(STE_FRAG_LAST); 2009c8befdd5SWarner Losh /* 2010a1b2c209SPyun YongHyeon * Because we use Tx polling we can't chain multiple 2011a1b2c209SPyun YongHyeon * Tx descriptors here. Otherwise we race with controller. 2012c8befdd5SWarner Losh */ 2013a1b2c209SPyun YongHyeon desc->ste_next = 0; 2014ae49e7a6SPyun YongHyeon if ((sc->ste_cdata.ste_tx_prod % STE_TX_INTR_FRAMES) == 0) 2015ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS | 2016ae49e7a6SPyun YongHyeon STE_TXCTL_DMAINTR); 2017ae49e7a6SPyun YongHyeon else 2018ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS); 2019a1b2c209SPyun YongHyeon txc->ste_mbuf = *m_head; 2020a1b2c209SPyun YongHyeon STE_INC(sc->ste_cdata.ste_tx_prod, STE_TX_LIST_CNT); 2021a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt++; 2022c8befdd5SWarner Losh 2023c8befdd5SWarner Losh return (0); 2024c8befdd5SWarner Losh } 2025c8befdd5SWarner Losh 2026c8befdd5SWarner Losh static void 202760270842SPyun YongHyeon ste_start(struct ifnet *ifp) 2028c8befdd5SWarner Losh { 2029c8befdd5SWarner Losh struct ste_softc *sc; 2030c8befdd5SWarner Losh 2031c8befdd5SWarner Losh sc = ifp->if_softc; 2032c8befdd5SWarner Losh STE_LOCK(sc); 2033c8befdd5SWarner Losh ste_start_locked(ifp); 2034c8befdd5SWarner Losh STE_UNLOCK(sc); 2035c8befdd5SWarner Losh } 2036c8befdd5SWarner Losh 2037c8befdd5SWarner Losh static void 203860270842SPyun YongHyeon ste_start_locked(struct ifnet *ifp) 2039c8befdd5SWarner Losh { 2040c8befdd5SWarner Losh struct ste_softc *sc; 2041c8befdd5SWarner Losh struct ste_chain *cur_tx; 2042f2632c3bSPyun YongHyeon struct mbuf *m_head = NULL; 2043a1b2c209SPyun YongHyeon int enq; 2044c8befdd5SWarner Losh 2045c8befdd5SWarner Losh sc = ifp->if_softc; 2046c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 2047c8befdd5SWarner Losh 20484465097bSPyun YongHyeon if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 20494465097bSPyun YongHyeon IFF_DRV_RUNNING || (sc->ste_flags & STE_FLAG_LINK) == 0) 2050c8befdd5SWarner Losh return; 2051c8befdd5SWarner Losh 2052a1b2c209SPyun YongHyeon for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { 2053a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == STE_TX_LIST_CNT - 1) { 2054c8befdd5SWarner Losh /* 2055a1b2c209SPyun YongHyeon * Controller may have cached copy of the last used 2056a1b2c209SPyun YongHyeon * next ptr so we have to reserve one TFD to avoid 2057a1b2c209SPyun YongHyeon * TFD overruns. 2058c8befdd5SWarner Losh */ 2059c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2060c8befdd5SWarner Losh break; 2061c8befdd5SWarner Losh } 2062c8befdd5SWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 2063c8befdd5SWarner Losh if (m_head == NULL) 2064c8befdd5SWarner Losh break; 2065a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[sc->ste_cdata.ste_tx_prod]; 2066a1b2c209SPyun YongHyeon if (ste_encap(sc, &m_head, cur_tx) != 0) { 2067a1b2c209SPyun YongHyeon if (m_head == NULL) 2068c8befdd5SWarner Losh break; 2069a1b2c209SPyun YongHyeon IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 2070a1b2c209SPyun YongHyeon break; 2071a1b2c209SPyun YongHyeon } 2072a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_last_tx == NULL) { 2073a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 2074a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 2075a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2076c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 2077c8befdd5SWarner Losh ste_wait(sc); 2078c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 2079a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_tx_list_paddr)); 2080c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); 2081c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 2082c8befdd5SWarner Losh ste_wait(sc); 2083c8befdd5SWarner Losh } else { 2084a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_ptr->ste_next = 2085a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_phys; 2086a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 2087a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 2088a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2089c8befdd5SWarner Losh } 2090a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx = cur_tx; 2091c8befdd5SWarner Losh 2092a1b2c209SPyun YongHyeon enq++; 2093c8befdd5SWarner Losh /* 2094c8befdd5SWarner Losh * If there's a BPF listener, bounce a copy of this frame 2095c8befdd5SWarner Losh * to him. 2096c8befdd5SWarner Losh */ 2097a1b2c209SPyun YongHyeon BPF_MTAP(ifp, m_head); 2098c8befdd5SWarner Losh } 2099a1b2c209SPyun YongHyeon 2100a1b2c209SPyun YongHyeon if (enq > 0) 2101a1b2c209SPyun YongHyeon sc->ste_timer = STE_TX_TIMEOUT; 2102c8befdd5SWarner Losh } 2103c8befdd5SWarner Losh 2104c8befdd5SWarner Losh static void 21057cf545d0SJohn Baldwin ste_watchdog(struct ste_softc *sc) 2106c8befdd5SWarner Losh { 21077cf545d0SJohn Baldwin struct ifnet *ifp; 2108c8befdd5SWarner Losh 21097cf545d0SJohn Baldwin ifp = sc->ste_ifp; 21107cf545d0SJohn Baldwin STE_LOCK_ASSERT(sc); 2111c8befdd5SWarner Losh 211210f695eeSPyun YongHyeon if (sc->ste_timer == 0 || --sc->ste_timer) 211310f695eeSPyun YongHyeon return; 211410f695eeSPyun YongHyeon 2115c8befdd5SWarner Losh ifp->if_oerrors++; 2116c8befdd5SWarner Losh if_printf(ifp, "watchdog timeout\n"); 2117c8befdd5SWarner Losh 2118c8befdd5SWarner Losh ste_txeof(sc); 211981598b3eSPyun YongHyeon ste_txeoc(sc); 2120a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 212155d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2122c8befdd5SWarner Losh ste_init_locked(sc); 2123c8befdd5SWarner Losh 2124c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 2125c8befdd5SWarner Losh ste_start_locked(ifp); 2126c8befdd5SWarner Losh } 2127c8befdd5SWarner Losh 2128c8befdd5SWarner Losh static int 212960270842SPyun YongHyeon ste_shutdown(device_t dev) 2130c8befdd5SWarner Losh { 2131b4c170e1SPyun YongHyeon 2132b4c170e1SPyun YongHyeon return (ste_suspend(dev)); 2133b4c170e1SPyun YongHyeon } 2134b4c170e1SPyun YongHyeon 2135b4c170e1SPyun YongHyeon static int 2136b4c170e1SPyun YongHyeon ste_suspend(device_t dev) 2137b4c170e1SPyun YongHyeon { 2138c8befdd5SWarner Losh struct ste_softc *sc; 2139c8befdd5SWarner Losh 2140c8befdd5SWarner Losh sc = device_get_softc(dev); 2141c8befdd5SWarner Losh 2142c8befdd5SWarner Losh STE_LOCK(sc); 2143c8befdd5SWarner Losh ste_stop(sc); 2144b4c170e1SPyun YongHyeon ste_setwol(sc); 2145b4c170e1SPyun YongHyeon STE_UNLOCK(sc); 2146b4c170e1SPyun YongHyeon 2147b4c170e1SPyun YongHyeon return (0); 2148b4c170e1SPyun YongHyeon } 2149b4c170e1SPyun YongHyeon 2150b4c170e1SPyun YongHyeon static int 2151b4c170e1SPyun YongHyeon ste_resume(device_t dev) 2152b4c170e1SPyun YongHyeon { 2153b4c170e1SPyun YongHyeon struct ste_softc *sc; 2154b4c170e1SPyun YongHyeon struct ifnet *ifp; 2155b4c170e1SPyun YongHyeon int pmc; 2156b4c170e1SPyun YongHyeon uint16_t pmstat; 2157b4c170e1SPyun YongHyeon 2158b4c170e1SPyun YongHyeon sc = device_get_softc(dev); 2159b4c170e1SPyun YongHyeon STE_LOCK(sc); 2160b4c170e1SPyun YongHyeon if (pci_find_extcap(sc->ste_dev, PCIY_PMG, &pmc) == 0) { 2161b4c170e1SPyun YongHyeon /* Disable PME and clear PME status. */ 2162b4c170e1SPyun YongHyeon pmstat = pci_read_config(sc->ste_dev, 2163b4c170e1SPyun YongHyeon pmc + PCIR_POWER_STATUS, 2); 2164b4c170e1SPyun YongHyeon if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { 2165b4c170e1SPyun YongHyeon pmstat &= ~PCIM_PSTAT_PMEENABLE; 2166b4c170e1SPyun YongHyeon pci_write_config(sc->ste_dev, 2167b4c170e1SPyun YongHyeon pmc + PCIR_POWER_STATUS, pmstat, 2); 2168b4c170e1SPyun YongHyeon } 2169b4c170e1SPyun YongHyeon } 2170b4c170e1SPyun YongHyeon ifp = sc->ste_ifp; 2171b4c170e1SPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 2172b4c170e1SPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2173b4c170e1SPyun YongHyeon ste_init_locked(sc); 2174b4c170e1SPyun YongHyeon } 2175c8befdd5SWarner Losh STE_UNLOCK(sc); 2176c8befdd5SWarner Losh 2177c8befdd5SWarner Losh return (0); 2178c8befdd5SWarner Losh } 21798657caa6SPyun YongHyeon 21808657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 21818657caa6SPyun YongHyeon SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 21828657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2183*6dc7dc9aSMatthew D Fleming SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 21848657caa6SPyun YongHyeon 21858657caa6SPyun YongHyeon static void 21868657caa6SPyun YongHyeon ste_sysctl_node(struct ste_softc *sc) 21878657caa6SPyun YongHyeon { 21888657caa6SPyun YongHyeon struct sysctl_ctx_list *ctx; 21898657caa6SPyun YongHyeon struct sysctl_oid_list *child, *parent; 21908657caa6SPyun YongHyeon struct sysctl_oid *tree; 21918657caa6SPyun YongHyeon struct ste_hw_stats *stats; 21928657caa6SPyun YongHyeon 21938657caa6SPyun YongHyeon stats = &sc->ste_stats; 21948657caa6SPyun YongHyeon ctx = device_get_sysctl_ctx(sc->ste_dev); 21958657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ste_dev)); 21968657caa6SPyun YongHyeon 2197fabbaac5SPyun YongHyeon SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_rx_mod", 2198fabbaac5SPyun YongHyeon CTLFLAG_RW, &sc->ste_int_rx_mod, 0, "ste RX interrupt moderation"); 2199fabbaac5SPyun YongHyeon /* Pull in device tunables. */ 2200fabbaac5SPyun YongHyeon sc->ste_int_rx_mod = STE_IM_RX_TIMER_DEFAULT; 2201fabbaac5SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev), 2202fabbaac5SPyun YongHyeon device_get_unit(sc->ste_dev), "int_rx_mod", &sc->ste_int_rx_mod); 2203fabbaac5SPyun YongHyeon 22048657caa6SPyun YongHyeon tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, 22058657caa6SPyun YongHyeon NULL, "STE statistics"); 22068657caa6SPyun YongHyeon parent = SYSCTL_CHILDREN(tree); 22078657caa6SPyun YongHyeon 22088657caa6SPyun YongHyeon /* Rx statistics. */ 22098657caa6SPyun YongHyeon tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, 22108657caa6SPyun YongHyeon NULL, "Rx MAC statistics"); 22118657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree); 22128657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets", 22138657caa6SPyun YongHyeon &stats->rx_bytes, "Good octets"); 22148657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 22158657caa6SPyun YongHyeon &stats->rx_frames, "Good frames"); 22168657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 22178657caa6SPyun YongHyeon &stats->rx_bcast_frames, "Good broadcast frames"); 22188657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 22198657caa6SPyun YongHyeon &stats->rx_mcast_frames, "Good multicast frames"); 22208657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "lost_frames", 22218657caa6SPyun YongHyeon &stats->rx_lost_frames, "Lost frames"); 22228657caa6SPyun YongHyeon 22238657caa6SPyun YongHyeon /* Tx statistics. */ 22248657caa6SPyun YongHyeon tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, 22258657caa6SPyun YongHyeon NULL, "Tx MAC statistics"); 22268657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree); 22278657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets", 22288657caa6SPyun YongHyeon &stats->tx_bytes, "Good octets"); 22298657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 22308657caa6SPyun YongHyeon &stats->tx_frames, "Good frames"); 22318657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 22328657caa6SPyun YongHyeon &stats->tx_bcast_frames, "Good broadcast frames"); 22338657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 22348657caa6SPyun YongHyeon &stats->tx_mcast_frames, "Good multicast frames"); 22358657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "carrier_errs", 22368657caa6SPyun YongHyeon &stats->tx_carrsense_errs, "Carrier sense errors"); 22378657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "single_colls", 22388657caa6SPyun YongHyeon &stats->tx_single_colls, "Single collisions"); 22398657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "multi_colls", 22408657caa6SPyun YongHyeon &stats->tx_multi_colls, "Multiple collisions"); 22418657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 22428657caa6SPyun YongHyeon &stats->tx_late_colls, "Late collisions"); 22438657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "defers", 22448657caa6SPyun YongHyeon &stats->tx_frames_defered, "Frames with deferrals"); 22458657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "excess_defers", 22468657caa6SPyun YongHyeon &stats->tx_excess_defers, "Frames with excessive derferrals"); 22478657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "abort", 22488657caa6SPyun YongHyeon &stats->tx_abort, "Aborted frames due to Excessive collisions"); 22498657caa6SPyun YongHyeon } 22508657caa6SPyun YongHyeon 22518657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD32 22528657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD64 2253b4c170e1SPyun YongHyeon 2254b4c170e1SPyun YongHyeon static void 2255b4c170e1SPyun YongHyeon ste_setwol(struct ste_softc *sc) 2256b4c170e1SPyun YongHyeon { 2257b4c170e1SPyun YongHyeon struct ifnet *ifp; 2258b4c170e1SPyun YongHyeon uint16_t pmstat; 2259b4c170e1SPyun YongHyeon uint8_t val; 2260b4c170e1SPyun YongHyeon int pmc; 2261b4c170e1SPyun YongHyeon 2262b4c170e1SPyun YongHyeon STE_LOCK_ASSERT(sc); 2263b4c170e1SPyun YongHyeon 2264b4c170e1SPyun YongHyeon if (pci_find_extcap(sc->ste_dev, PCIY_PMG, &pmc) != 0) { 2265b4c170e1SPyun YongHyeon /* Disable WOL. */ 2266b4c170e1SPyun YongHyeon CSR_READ_1(sc, STE_WAKE_EVENT); 2267b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, 0); 2268b4c170e1SPyun YongHyeon return; 2269b4c170e1SPyun YongHyeon } 2270b4c170e1SPyun YongHyeon 2271b4c170e1SPyun YongHyeon ifp = sc->ste_ifp; 2272b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT); 2273b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB | 2274b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB); 2275b4c170e1SPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2276b4c170e1SPyun YongHyeon val |= STE_WAKEEVENT_MAGICPKT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB; 2277b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val); 2278b4c170e1SPyun YongHyeon /* Request PME. */ 2279b4c170e1SPyun YongHyeon pmstat = pci_read_config(sc->ste_dev, pmc + PCIR_POWER_STATUS, 2); 2280b4c170e1SPyun YongHyeon pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); 2281b4c170e1SPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2282b4c170e1SPyun YongHyeon pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; 2283b4c170e1SPyun YongHyeon pci_write_config(sc->ste_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); 2284b4c170e1SPyun YongHyeon } 2285