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> 42c8befdd5SWarner Losh #include <sys/sockio.h> 43c8befdd5SWarner Losh #include <sys/mbuf.h> 44c8befdd5SWarner Losh #include <sys/malloc.h> 45c8befdd5SWarner Losh #include <sys/kernel.h> 46c8befdd5SWarner Losh #include <sys/module.h> 47c8befdd5SWarner Losh #include <sys/socket.h> 48c8befdd5SWarner Losh #include <sys/sysctl.h> 49c8befdd5SWarner Losh 50c8befdd5SWarner Losh #include <net/if.h> 51c8befdd5SWarner Losh #include <net/if_arp.h> 52c8befdd5SWarner Losh #include <net/ethernet.h> 53c8befdd5SWarner Losh #include <net/if_dl.h> 54c8befdd5SWarner Losh #include <net/if_media.h> 55c8befdd5SWarner Losh #include <net/if_types.h> 56c8befdd5SWarner Losh #include <net/if_vlan_var.h> 57c8befdd5SWarner Losh 58c8befdd5SWarner Losh #include <net/bpf.h> 59c8befdd5SWarner Losh 60c8befdd5SWarner Losh #include <vm/vm.h> /* for vtophys */ 61c8befdd5SWarner Losh #include <vm/pmap.h> /* for vtophys */ 62c8befdd5SWarner Losh #include <machine/bus.h> 63c8befdd5SWarner Losh #include <machine/resource.h> 64c8befdd5SWarner Losh #include <sys/bus.h> 65c8befdd5SWarner Losh #include <sys/rman.h> 66c8befdd5SWarner Losh 67c8befdd5SWarner Losh #include <dev/mii/mii.h> 68c8befdd5SWarner Losh #include <dev/mii/miivar.h> 69c8befdd5SWarner Losh 70c8befdd5SWarner Losh #include <dev/pci/pcireg.h> 71c8befdd5SWarner Losh #include <dev/pci/pcivar.h> 72c8befdd5SWarner Losh 73c8befdd5SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 74c8befdd5SWarner Losh #include "miibus_if.h" 75c8befdd5SWarner Losh 76c8befdd5SWarner Losh #define STE_USEIOSPACE 77c8befdd5SWarner Losh 78c8befdd5SWarner Losh #include <dev/ste/if_stereg.h> 79c8befdd5SWarner Losh 80c8befdd5SWarner Losh MODULE_DEPEND(ste, pci, 1, 1, 1); 81c8befdd5SWarner Losh MODULE_DEPEND(ste, ether, 1, 1, 1); 82c8befdd5SWarner Losh MODULE_DEPEND(ste, miibus, 1, 1, 1); 83c8befdd5SWarner Losh 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_probe(device_t); 95c8befdd5SWarner Losh static int ste_attach(device_t); 96c8befdd5SWarner Losh static int ste_detach(device_t); 97c8befdd5SWarner Losh static void ste_init(void *); 98c8befdd5SWarner Losh static void ste_init_locked(struct ste_softc *); 99c8befdd5SWarner Losh static void ste_intr(void *); 100c8befdd5SWarner Losh static void ste_rxeoc(struct ste_softc *); 1011abcdbd1SAttilio Rao static int ste_rxeof(struct ste_softc *); 102c8befdd5SWarner Losh static void ste_txeoc(struct ste_softc *); 103c8befdd5SWarner Losh static void ste_txeof(struct ste_softc *); 104c8befdd5SWarner Losh static void ste_stats_update(void *); 105c8befdd5SWarner Losh static void ste_stop(struct ste_softc *); 106c8befdd5SWarner Losh static void ste_reset(struct ste_softc *); 107c8befdd5SWarner Losh static int ste_ioctl(struct ifnet *, u_long, caddr_t); 108c8befdd5SWarner Losh static int ste_encap(struct ste_softc *, struct ste_chain *, struct mbuf *); 109c8befdd5SWarner Losh static void ste_start(struct ifnet *); 110c8befdd5SWarner Losh static void ste_start_locked(struct ifnet *); 111c8befdd5SWarner Losh static void ste_watchdog(struct ifnet *); 112c8befdd5SWarner Losh static int ste_shutdown(device_t); 113c8befdd5SWarner Losh static int ste_newbuf(struct ste_softc *, struct ste_chain_onefrag *, 114c8befdd5SWarner Losh struct mbuf *); 115c8befdd5SWarner Losh static int ste_ifmedia_upd(struct ifnet *); 116c8befdd5SWarner Losh static void ste_ifmedia_upd_locked(struct ifnet *); 117c8befdd5SWarner Losh static void ste_ifmedia_sts(struct ifnet *, struct ifmediareq *); 118c8befdd5SWarner Losh 119c8befdd5SWarner Losh static void ste_mii_sync(struct ste_softc *); 120c8befdd5SWarner Losh static void ste_mii_send(struct ste_softc *, u_int32_t, int); 121c8befdd5SWarner Losh static int ste_mii_readreg(struct ste_softc *, struct ste_mii_frame *); 122c8befdd5SWarner Losh static int ste_mii_writereg(struct ste_softc *, struct ste_mii_frame *); 123c8befdd5SWarner Losh static int ste_miibus_readreg(device_t, int, int); 124c8befdd5SWarner Losh static int ste_miibus_writereg(device_t, int, int, int); 125c8befdd5SWarner Losh static void ste_miibus_statchg(device_t); 126c8befdd5SWarner Losh 127c8befdd5SWarner Losh static int ste_eeprom_wait(struct ste_softc *); 128c8befdd5SWarner Losh static int ste_read_eeprom(struct ste_softc *, caddr_t, int, int, int); 129c8befdd5SWarner Losh static void ste_wait(struct ste_softc *); 130c8befdd5SWarner Losh static void ste_setmulti(struct ste_softc *); 131c8befdd5SWarner Losh static int ste_init_rx_list(struct ste_softc *); 132c8befdd5SWarner Losh static void ste_init_tx_list(struct ste_softc *); 133c8befdd5SWarner Losh 134c8befdd5SWarner Losh #ifdef STE_USEIOSPACE 135c8befdd5SWarner Losh #define STE_RES SYS_RES_IOPORT 136c8befdd5SWarner Losh #define STE_RID STE_PCI_LOIO 137c8befdd5SWarner Losh #else 138c8befdd5SWarner Losh #define STE_RES SYS_RES_MEMORY 139c8befdd5SWarner Losh #define STE_RID STE_PCI_LOMEM 140c8befdd5SWarner Losh #endif 141c8befdd5SWarner Losh 142c8befdd5SWarner Losh static device_method_t ste_methods[] = { 143c8befdd5SWarner Losh /* Device interface */ 144c8befdd5SWarner Losh DEVMETHOD(device_probe, ste_probe), 145c8befdd5SWarner Losh DEVMETHOD(device_attach, ste_attach), 146c8befdd5SWarner Losh DEVMETHOD(device_detach, ste_detach), 147c8befdd5SWarner Losh DEVMETHOD(device_shutdown, ste_shutdown), 148c8befdd5SWarner Losh 149c8befdd5SWarner Losh /* bus interface */ 150c8befdd5SWarner Losh DEVMETHOD(bus_print_child, bus_generic_print_child), 151c8befdd5SWarner Losh DEVMETHOD(bus_driver_added, bus_generic_driver_added), 152c8befdd5SWarner Losh 153c8befdd5SWarner Losh /* MII interface */ 154c8befdd5SWarner Losh DEVMETHOD(miibus_readreg, ste_miibus_readreg), 155c8befdd5SWarner Losh DEVMETHOD(miibus_writereg, ste_miibus_writereg), 156c8befdd5SWarner Losh DEVMETHOD(miibus_statchg, ste_miibus_statchg), 157c8befdd5SWarner Losh 158c8befdd5SWarner Losh { 0, 0 } 159c8befdd5SWarner Losh }; 160c8befdd5SWarner Losh 161c8befdd5SWarner Losh static driver_t ste_driver = { 162c8befdd5SWarner Losh "ste", 163c8befdd5SWarner Losh ste_methods, 164c8befdd5SWarner Losh sizeof(struct ste_softc) 165c8befdd5SWarner Losh }; 166c8befdd5SWarner Losh 167c8befdd5SWarner Losh static devclass_t ste_devclass; 168c8befdd5SWarner Losh 169c8befdd5SWarner Losh DRIVER_MODULE(ste, pci, ste_driver, ste_devclass, 0, 0); 170c8befdd5SWarner Losh DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); 171c8befdd5SWarner Losh 172c8befdd5SWarner Losh SYSCTL_NODE(_hw, OID_AUTO, ste, CTLFLAG_RD, 0, "if_ste parameters"); 173c8befdd5SWarner Losh 174c8befdd5SWarner Losh static int ste_rxsyncs; 175c8befdd5SWarner Losh SYSCTL_INT(_hw_ste, OID_AUTO, rxsyncs, CTLFLAG_RW, &ste_rxsyncs, 0, ""); 176c8befdd5SWarner Losh 177c8befdd5SWarner Losh #define STE_SETBIT4(sc, reg, x) \ 178c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) 179c8befdd5SWarner Losh 180c8befdd5SWarner Losh #define STE_CLRBIT4(sc, reg, x) \ 181c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) 182c8befdd5SWarner Losh 183c8befdd5SWarner Losh #define STE_SETBIT2(sc, reg, x) \ 184c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x)) 185c8befdd5SWarner Losh 186c8befdd5SWarner Losh #define STE_CLRBIT2(sc, reg, x) \ 187c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x)) 188c8befdd5SWarner Losh 189c8befdd5SWarner Losh #define STE_SETBIT1(sc, reg, x) \ 190c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) 191c8befdd5SWarner Losh 192c8befdd5SWarner Losh #define STE_CLRBIT1(sc, reg, x) \ 193c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x)) 194c8befdd5SWarner Losh 195c8befdd5SWarner Losh 196c8befdd5SWarner Losh #define MII_SET(x) STE_SETBIT1(sc, STE_PHYCTL, x) 197c8befdd5SWarner Losh #define MII_CLR(x) STE_CLRBIT1(sc, STE_PHYCTL, x) 198c8befdd5SWarner Losh 199c8befdd5SWarner Losh /* 200c8befdd5SWarner Losh * Sync the PHYs by setting data bit and strobing the clock 32 times. 201c8befdd5SWarner Losh */ 202c8befdd5SWarner Losh static void 203c8befdd5SWarner Losh ste_mii_sync(sc) 204c8befdd5SWarner Losh struct ste_softc *sc; 205c8befdd5SWarner Losh { 206c8befdd5SWarner Losh register int i; 207c8befdd5SWarner Losh 208c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR|STE_PHYCTL_MDATA); 209c8befdd5SWarner Losh 210c8befdd5SWarner Losh for (i = 0; i < 32; i++) { 211c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 212c8befdd5SWarner Losh DELAY(1); 213c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 214c8befdd5SWarner Losh DELAY(1); 215c8befdd5SWarner Losh } 216c8befdd5SWarner Losh 217c8befdd5SWarner Losh return; 218c8befdd5SWarner Losh } 219c8befdd5SWarner Losh 220c8befdd5SWarner Losh /* 221c8befdd5SWarner Losh * Clock a series of bits through the MII. 222c8befdd5SWarner Losh */ 223c8befdd5SWarner Losh static void 224c8befdd5SWarner Losh ste_mii_send(sc, bits, cnt) 225c8befdd5SWarner Losh struct ste_softc *sc; 226c8befdd5SWarner Losh u_int32_t bits; 227c8befdd5SWarner Losh int cnt; 228c8befdd5SWarner Losh { 229c8befdd5SWarner Losh int i; 230c8befdd5SWarner Losh 231c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 232c8befdd5SWarner Losh 233c8befdd5SWarner Losh for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 234c8befdd5SWarner Losh if (bits & i) { 235c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDATA); 236c8befdd5SWarner Losh } else { 237c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDATA); 238c8befdd5SWarner Losh } 239c8befdd5SWarner Losh DELAY(1); 240c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 241c8befdd5SWarner Losh DELAY(1); 242c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 243c8befdd5SWarner Losh } 244c8befdd5SWarner Losh } 245c8befdd5SWarner Losh 246c8befdd5SWarner Losh /* 247c8befdd5SWarner Losh * Read an PHY register through the MII. 248c8befdd5SWarner Losh */ 249c8befdd5SWarner Losh static int 250c8befdd5SWarner Losh ste_mii_readreg(sc, frame) 251c8befdd5SWarner Losh struct ste_softc *sc; 252c8befdd5SWarner Losh struct ste_mii_frame *frame; 253c8befdd5SWarner Losh 254c8befdd5SWarner Losh { 255c8befdd5SWarner Losh int i, ack; 256c8befdd5SWarner Losh 257c8befdd5SWarner Losh /* 258c8befdd5SWarner Losh * Set up frame for RX. 259c8befdd5SWarner Losh */ 260c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 261c8befdd5SWarner Losh frame->mii_opcode = STE_MII_READOP; 262c8befdd5SWarner Losh frame->mii_turnaround = 0; 263c8befdd5SWarner Losh frame->mii_data = 0; 264c8befdd5SWarner Losh 265c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PHYCTL, 0); 266c8befdd5SWarner Losh /* 267c8befdd5SWarner Losh * Turn on data xmit. 268c8befdd5SWarner Losh */ 269c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 270c8befdd5SWarner Losh 271c8befdd5SWarner Losh ste_mii_sync(sc); 272c8befdd5SWarner Losh 273c8befdd5SWarner Losh /* 274c8befdd5SWarner Losh * Send command/address info. 275c8befdd5SWarner Losh */ 276c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 277c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 278c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 279c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 280c8befdd5SWarner Losh 281c8befdd5SWarner Losh /* Turn off xmit. */ 282c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 283c8befdd5SWarner Losh 284c8befdd5SWarner Losh /* Idle bit */ 285c8befdd5SWarner Losh MII_CLR((STE_PHYCTL_MCLK|STE_PHYCTL_MDATA)); 286c8befdd5SWarner Losh DELAY(1); 287c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 288c8befdd5SWarner Losh DELAY(1); 289c8befdd5SWarner Losh 290c8befdd5SWarner Losh /* Check for ack */ 291c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 292c8befdd5SWarner Losh DELAY(1); 293c8befdd5SWarner Losh ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; 294c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 295c8befdd5SWarner Losh DELAY(1); 296c8befdd5SWarner Losh 297c8befdd5SWarner Losh /* 298c8befdd5SWarner Losh * Now try reading data bits. If the ack failed, we still 299c8befdd5SWarner Losh * need to clock through 16 cycles to keep the PHY(s) in sync. 300c8befdd5SWarner Losh */ 301c8befdd5SWarner Losh if (ack) { 302c8befdd5SWarner Losh for(i = 0; i < 16; i++) { 303c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 304c8befdd5SWarner Losh DELAY(1); 305c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 306c8befdd5SWarner Losh DELAY(1); 307c8befdd5SWarner Losh } 308c8befdd5SWarner Losh goto fail; 309c8befdd5SWarner Losh } 310c8befdd5SWarner Losh 311c8befdd5SWarner Losh for (i = 0x8000; i; i >>= 1) { 312c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 313c8befdd5SWarner Losh DELAY(1); 314c8befdd5SWarner Losh if (!ack) { 315c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA) 316c8befdd5SWarner Losh frame->mii_data |= i; 317c8befdd5SWarner Losh DELAY(1); 318c8befdd5SWarner Losh } 319c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 320c8befdd5SWarner Losh DELAY(1); 321c8befdd5SWarner Losh } 322c8befdd5SWarner Losh 323c8befdd5SWarner Losh fail: 324c8befdd5SWarner Losh 325c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 326c8befdd5SWarner Losh DELAY(1); 327c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 328c8befdd5SWarner Losh DELAY(1); 329c8befdd5SWarner Losh 330c8befdd5SWarner Losh if (ack) 331c8befdd5SWarner Losh return(1); 332c8befdd5SWarner Losh return(0); 333c8befdd5SWarner Losh } 334c8befdd5SWarner Losh 335c8befdd5SWarner Losh /* 336c8befdd5SWarner Losh * Write to a PHY register through the MII. 337c8befdd5SWarner Losh */ 338c8befdd5SWarner Losh static int 339c8befdd5SWarner Losh ste_mii_writereg(sc, frame) 340c8befdd5SWarner Losh struct ste_softc *sc; 341c8befdd5SWarner Losh struct ste_mii_frame *frame; 342c8befdd5SWarner Losh 343c8befdd5SWarner Losh { 344c8befdd5SWarner Losh 345c8befdd5SWarner Losh /* 346c8befdd5SWarner Losh * Set up frame for TX. 347c8befdd5SWarner Losh */ 348c8befdd5SWarner Losh 349c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 350c8befdd5SWarner Losh frame->mii_opcode = STE_MII_WRITEOP; 351c8befdd5SWarner Losh frame->mii_turnaround = STE_MII_TURNAROUND; 352c8befdd5SWarner Losh 353c8befdd5SWarner Losh /* 354c8befdd5SWarner Losh * Turn on data output. 355c8befdd5SWarner Losh */ 356c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 357c8befdd5SWarner Losh 358c8befdd5SWarner Losh ste_mii_sync(sc); 359c8befdd5SWarner Losh 360c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 361c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 362c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 363c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 364c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_turnaround, 2); 365c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_data, 16); 366c8befdd5SWarner Losh 367c8befdd5SWarner Losh /* Idle bit. */ 368c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 369c8befdd5SWarner Losh DELAY(1); 370c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 371c8befdd5SWarner Losh DELAY(1); 372c8befdd5SWarner Losh 373c8befdd5SWarner Losh /* 374c8befdd5SWarner Losh * Turn off xmit. 375c8befdd5SWarner Losh */ 376c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 377c8befdd5SWarner Losh 378c8befdd5SWarner Losh return(0); 379c8befdd5SWarner Losh } 380c8befdd5SWarner Losh 381c8befdd5SWarner Losh static int 382c8befdd5SWarner Losh ste_miibus_readreg(dev, phy, reg) 383c8befdd5SWarner Losh device_t dev; 384c8befdd5SWarner Losh int phy, reg; 385c8befdd5SWarner Losh { 386c8befdd5SWarner Losh struct ste_softc *sc; 387c8befdd5SWarner Losh struct ste_mii_frame frame; 388c8befdd5SWarner Losh 389c8befdd5SWarner Losh sc = device_get_softc(dev); 390c8befdd5SWarner Losh 391c8befdd5SWarner Losh if ( sc->ste_one_phy && phy != 0 ) 392c8befdd5SWarner Losh return (0); 393c8befdd5SWarner Losh 394c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 395c8befdd5SWarner Losh 396c8befdd5SWarner Losh frame.mii_phyaddr = phy; 397c8befdd5SWarner Losh frame.mii_regaddr = reg; 398c8befdd5SWarner Losh ste_mii_readreg(sc, &frame); 399c8befdd5SWarner Losh 400c8befdd5SWarner Losh return(frame.mii_data); 401c8befdd5SWarner Losh } 402c8befdd5SWarner Losh 403c8befdd5SWarner Losh static int 404c8befdd5SWarner Losh ste_miibus_writereg(dev, phy, reg, data) 405c8befdd5SWarner Losh device_t dev; 406c8befdd5SWarner Losh int phy, reg, data; 407c8befdd5SWarner Losh { 408c8befdd5SWarner Losh struct ste_softc *sc; 409c8befdd5SWarner Losh struct ste_mii_frame frame; 410c8befdd5SWarner Losh 411c8befdd5SWarner Losh sc = device_get_softc(dev); 412c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 413c8befdd5SWarner Losh 414c8befdd5SWarner Losh frame.mii_phyaddr = phy; 415c8befdd5SWarner Losh frame.mii_regaddr = reg; 416c8befdd5SWarner Losh frame.mii_data = data; 417c8befdd5SWarner Losh 418c8befdd5SWarner Losh ste_mii_writereg(sc, &frame); 419c8befdd5SWarner Losh 420c8befdd5SWarner Losh return(0); 421c8befdd5SWarner Losh } 422c8befdd5SWarner Losh 423c8befdd5SWarner Losh static void 424c8befdd5SWarner Losh ste_miibus_statchg(dev) 425c8befdd5SWarner Losh device_t dev; 426c8befdd5SWarner Losh { 427c8befdd5SWarner Losh struct ste_softc *sc; 428c8befdd5SWarner Losh struct mii_data *mii; 429c8befdd5SWarner Losh 430c8befdd5SWarner Losh sc = device_get_softc(dev); 431c8befdd5SWarner Losh 432c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 433c8befdd5SWarner Losh 434c8befdd5SWarner Losh if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 435c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); 436c8befdd5SWarner Losh } else { 437c8befdd5SWarner Losh STE_CLRBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); 438c8befdd5SWarner Losh } 439c8befdd5SWarner Losh 440c8befdd5SWarner Losh return; 441c8befdd5SWarner Losh } 442c8befdd5SWarner Losh 443c8befdd5SWarner Losh static int 444c8befdd5SWarner Losh ste_ifmedia_upd(ifp) 445c8befdd5SWarner Losh struct ifnet *ifp; 446c8befdd5SWarner Losh { 447c8befdd5SWarner Losh struct ste_softc *sc; 448c8befdd5SWarner Losh 449c8befdd5SWarner Losh sc = ifp->if_softc; 450c8befdd5SWarner Losh STE_LOCK(sc); 451c8befdd5SWarner Losh ste_ifmedia_upd_locked(ifp); 452c8befdd5SWarner Losh STE_UNLOCK(sc); 453c8befdd5SWarner Losh 454c8befdd5SWarner Losh return(0); 455c8befdd5SWarner Losh } 456c8befdd5SWarner Losh 457c8befdd5SWarner Losh static void 458c8befdd5SWarner Losh ste_ifmedia_upd_locked(ifp) 459c8befdd5SWarner Losh struct ifnet *ifp; 460c8befdd5SWarner Losh { 461c8befdd5SWarner Losh struct ste_softc *sc; 462c8befdd5SWarner Losh struct mii_data *mii; 463c8befdd5SWarner Losh 464c8befdd5SWarner Losh sc = ifp->if_softc; 465c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 466c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 467c8befdd5SWarner Losh sc->ste_link = 0; 468c8befdd5SWarner Losh if (mii->mii_instance) { 469c8befdd5SWarner Losh struct mii_softc *miisc; 470c8befdd5SWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 471c8befdd5SWarner Losh mii_phy_reset(miisc); 472c8befdd5SWarner Losh } 473c8befdd5SWarner Losh mii_mediachg(mii); 474c8befdd5SWarner Losh } 475c8befdd5SWarner Losh 476c8befdd5SWarner Losh static void 477c8befdd5SWarner Losh ste_ifmedia_sts(ifp, ifmr) 478c8befdd5SWarner Losh struct ifnet *ifp; 479c8befdd5SWarner Losh struct ifmediareq *ifmr; 480c8befdd5SWarner Losh { 481c8befdd5SWarner Losh struct ste_softc *sc; 482c8befdd5SWarner Losh struct mii_data *mii; 483c8befdd5SWarner Losh 484c8befdd5SWarner Losh sc = ifp->if_softc; 485c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 486c8befdd5SWarner Losh 487c8befdd5SWarner Losh STE_LOCK(sc); 488c8befdd5SWarner Losh mii_pollstat(mii); 489c8befdd5SWarner Losh ifmr->ifm_active = mii->mii_media_active; 490c8befdd5SWarner Losh ifmr->ifm_status = mii->mii_media_status; 491c8befdd5SWarner Losh STE_UNLOCK(sc); 492c8befdd5SWarner Losh 493c8befdd5SWarner Losh return; 494c8befdd5SWarner Losh } 495c8befdd5SWarner Losh 496c8befdd5SWarner Losh static void 497c8befdd5SWarner Losh ste_wait(sc) 498c8befdd5SWarner Losh struct ste_softc *sc; 499c8befdd5SWarner Losh { 500c8befdd5SWarner Losh register int i; 501c8befdd5SWarner Losh 502c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 503c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) 504c8befdd5SWarner Losh break; 505c8befdd5SWarner Losh } 506c8befdd5SWarner Losh 507c8befdd5SWarner Losh if (i == STE_TIMEOUT) 508c8befdd5SWarner Losh device_printf(sc->ste_dev, "command never completed!\n"); 509c8befdd5SWarner Losh 510c8befdd5SWarner Losh return; 511c8befdd5SWarner Losh } 512c8befdd5SWarner Losh 513c8befdd5SWarner Losh /* 514c8befdd5SWarner Losh * The EEPROM is slow: give it time to come ready after issuing 515c8befdd5SWarner Losh * it a command. 516c8befdd5SWarner Losh */ 517c8befdd5SWarner Losh static int 518c8befdd5SWarner Losh ste_eeprom_wait(sc) 519c8befdd5SWarner Losh struct ste_softc *sc; 520c8befdd5SWarner Losh { 521c8befdd5SWarner Losh int i; 522c8befdd5SWarner Losh 523c8befdd5SWarner Losh DELAY(1000); 524c8befdd5SWarner Losh 525c8befdd5SWarner Losh for (i = 0; i < 100; i++) { 526c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) 527c8befdd5SWarner Losh DELAY(1000); 528c8befdd5SWarner Losh else 529c8befdd5SWarner Losh break; 530c8befdd5SWarner Losh } 531c8befdd5SWarner Losh 532c8befdd5SWarner Losh if (i == 100) { 533c8befdd5SWarner Losh device_printf(sc->ste_dev, "eeprom failed to come ready\n"); 534c8befdd5SWarner Losh return(1); 535c8befdd5SWarner Losh } 536c8befdd5SWarner Losh 537c8befdd5SWarner Losh return(0); 538c8befdd5SWarner Losh } 539c8befdd5SWarner Losh 540c8befdd5SWarner Losh /* 541c8befdd5SWarner Losh * Read a sequence of words from the EEPROM. Note that ethernet address 542c8befdd5SWarner Losh * data is stored in the EEPROM in network byte order. 543c8befdd5SWarner Losh */ 544c8befdd5SWarner Losh static int 545c8befdd5SWarner Losh ste_read_eeprom(sc, dest, off, cnt, swap) 546c8befdd5SWarner Losh struct ste_softc *sc; 547c8befdd5SWarner Losh caddr_t dest; 548c8befdd5SWarner Losh int off; 549c8befdd5SWarner Losh int cnt; 550c8befdd5SWarner Losh int swap; 551c8befdd5SWarner Losh { 552c8befdd5SWarner Losh int err = 0, i; 553c8befdd5SWarner Losh u_int16_t word = 0, *ptr; 554c8befdd5SWarner Losh 555c8befdd5SWarner Losh if (ste_eeprom_wait(sc)) 556c8befdd5SWarner Losh return(1); 557c8befdd5SWarner Losh 558c8befdd5SWarner Losh for (i = 0; i < cnt; i++) { 559c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); 560c8befdd5SWarner Losh err = ste_eeprom_wait(sc); 561c8befdd5SWarner Losh if (err) 562c8befdd5SWarner Losh break; 563c8befdd5SWarner Losh word = CSR_READ_2(sc, STE_EEPROM_DATA); 564c8befdd5SWarner Losh ptr = (u_int16_t *)(dest + (i * 2)); 565c8befdd5SWarner Losh if (swap) 566c8befdd5SWarner Losh *ptr = ntohs(word); 567c8befdd5SWarner Losh else 568c8befdd5SWarner Losh *ptr = word; 569c8befdd5SWarner Losh } 570c8befdd5SWarner Losh 571c8befdd5SWarner Losh return(err ? 1 : 0); 572c8befdd5SWarner Losh } 573c8befdd5SWarner Losh 574c8befdd5SWarner Losh static void 575c8befdd5SWarner Losh ste_setmulti(sc) 576c8befdd5SWarner Losh struct ste_softc *sc; 577c8befdd5SWarner Losh { 578c8befdd5SWarner Losh struct ifnet *ifp; 579c8befdd5SWarner Losh int h = 0; 580c8befdd5SWarner Losh u_int32_t hashes[2] = { 0, 0 }; 581c8befdd5SWarner Losh struct ifmultiaddr *ifma; 582c8befdd5SWarner Losh 583c8befdd5SWarner Losh ifp = sc->ste_ifp; 584c8befdd5SWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 585c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); 586c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); 587c8befdd5SWarner Losh return; 588c8befdd5SWarner Losh } 589c8befdd5SWarner Losh 590c8befdd5SWarner Losh /* first, zot all the existing hash bits */ 591c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, 0); 592c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, 0); 593c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, 0); 594c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, 0); 595c8befdd5SWarner Losh 596c8befdd5SWarner Losh /* now program new ones */ 597c8befdd5SWarner Losh IF_ADDR_LOCK(ifp); 598c8befdd5SWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 599c8befdd5SWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 600c8befdd5SWarner Losh continue; 601c8befdd5SWarner Losh h = ether_crc32_be(LLADDR((struct sockaddr_dl *) 602c8befdd5SWarner Losh ifma->ifma_addr), ETHER_ADDR_LEN) & 0x3F; 603c8befdd5SWarner Losh if (h < 32) 604c8befdd5SWarner Losh hashes[0] |= (1 << h); 605c8befdd5SWarner Losh else 606c8befdd5SWarner Losh hashes[1] |= (1 << (h - 32)); 607c8befdd5SWarner Losh } 608c8befdd5SWarner Losh IF_ADDR_UNLOCK(ifp); 609c8befdd5SWarner Losh 610c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); 611c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); 612c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); 613c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); 614c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); 615c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); 616c8befdd5SWarner Losh 617c8befdd5SWarner Losh return; 618c8befdd5SWarner Losh } 619c8befdd5SWarner Losh 620c8befdd5SWarner Losh #ifdef DEVICE_POLLING 621c8befdd5SWarner Losh static poll_handler_t ste_poll, ste_poll_locked; 622c8befdd5SWarner Losh 6231abcdbd1SAttilio Rao static int 624c8befdd5SWarner Losh ste_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 625c8befdd5SWarner Losh { 626c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 6271abcdbd1SAttilio Rao int rx_npkts = 0; 628c8befdd5SWarner Losh 629c8befdd5SWarner Losh STE_LOCK(sc); 630c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6311abcdbd1SAttilio Rao rx_npkts = ste_poll_locked(ifp, cmd, count); 632c8befdd5SWarner Losh STE_UNLOCK(sc); 6331abcdbd1SAttilio Rao return (rx_npkts); 634c8befdd5SWarner Losh } 635c8befdd5SWarner Losh 6361abcdbd1SAttilio Rao static int 637c8befdd5SWarner Losh ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) 638c8befdd5SWarner Losh { 639c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 6401abcdbd1SAttilio Rao int rx_npkts; 641c8befdd5SWarner Losh 642c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 643c8befdd5SWarner Losh 644c8befdd5SWarner Losh sc->rxcycles = count; 645c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) 646c8befdd5SWarner Losh ste_rxeoc(sc); 6471abcdbd1SAttilio Rao rx_npkts = ste_rxeof(sc); 648c8befdd5SWarner Losh ste_txeof(sc); 649c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 650c8befdd5SWarner Losh ste_start_locked(ifp); 651c8befdd5SWarner Losh 652c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) { 653c8befdd5SWarner Losh u_int16_t status; 654c8befdd5SWarner Losh 655c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 656c8befdd5SWarner Losh 657c8befdd5SWarner Losh if (status & STE_ISR_TX_DONE) 658c8befdd5SWarner Losh ste_txeoc(sc); 659c8befdd5SWarner Losh 660c8befdd5SWarner Losh if (status & STE_ISR_STATS_OFLOW) { 661c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 662c8befdd5SWarner Losh ste_stats_update(sc); 663c8befdd5SWarner Losh } 664c8befdd5SWarner Losh 665c8befdd5SWarner Losh if (status & STE_ISR_LINKEVENT) 666c8befdd5SWarner Losh mii_pollstat(device_get_softc(sc->ste_miibus)); 667c8befdd5SWarner Losh 668c8befdd5SWarner Losh if (status & STE_ISR_HOSTERR) { 669c8befdd5SWarner Losh ste_reset(sc); 670c8befdd5SWarner Losh ste_init_locked(sc); 671c8befdd5SWarner Losh } 672c8befdd5SWarner Losh } 6731abcdbd1SAttilio Rao return (rx_npkts); 674c8befdd5SWarner Losh } 675c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 676c8befdd5SWarner Losh 677c8befdd5SWarner Losh static void 678c8befdd5SWarner Losh ste_intr(xsc) 679c8befdd5SWarner Losh void *xsc; 680c8befdd5SWarner Losh { 681c8befdd5SWarner Losh struct ste_softc *sc; 682c8befdd5SWarner Losh struct ifnet *ifp; 683c8befdd5SWarner Losh u_int16_t status; 684c8befdd5SWarner Losh 685c8befdd5SWarner Losh sc = xsc; 686c8befdd5SWarner Losh STE_LOCK(sc); 687c8befdd5SWarner Losh ifp = sc->ste_ifp; 688c8befdd5SWarner Losh 689c8befdd5SWarner Losh #ifdef DEVICE_POLLING 690c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 691c8befdd5SWarner Losh STE_UNLOCK(sc); 692c8befdd5SWarner Losh return; 693c8befdd5SWarner Losh } 694c8befdd5SWarner Losh #endif 695c8befdd5SWarner Losh 696c8befdd5SWarner Losh /* See if this is really our interrupt. */ 697c8befdd5SWarner Losh if (!(CSR_READ_2(sc, STE_ISR) & STE_ISR_INTLATCH)) { 698c8befdd5SWarner Losh STE_UNLOCK(sc); 699c8befdd5SWarner Losh return; 700c8befdd5SWarner Losh } 701c8befdd5SWarner Losh 702c8befdd5SWarner Losh for (;;) { 703c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 704c8befdd5SWarner Losh 705c8befdd5SWarner Losh if (!(status & STE_INTRS)) 706c8befdd5SWarner Losh break; 707c8befdd5SWarner Losh 708c8befdd5SWarner Losh if (status & STE_ISR_RX_DMADONE) { 709c8befdd5SWarner Losh ste_rxeoc(sc); 710c8befdd5SWarner Losh ste_rxeof(sc); 711c8befdd5SWarner Losh } 712c8befdd5SWarner Losh 713c8befdd5SWarner Losh if (status & STE_ISR_TX_DMADONE) 714c8befdd5SWarner Losh ste_txeof(sc); 715c8befdd5SWarner Losh 716c8befdd5SWarner Losh if (status & STE_ISR_TX_DONE) 717c8befdd5SWarner Losh ste_txeoc(sc); 718c8befdd5SWarner Losh 719c8befdd5SWarner Losh if (status & STE_ISR_STATS_OFLOW) { 720c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 721c8befdd5SWarner Losh ste_stats_update(sc); 722c8befdd5SWarner Losh } 723c8befdd5SWarner Losh 724c8befdd5SWarner Losh if (status & STE_ISR_LINKEVENT) 725c8befdd5SWarner Losh mii_pollstat(device_get_softc(sc->ste_miibus)); 726c8befdd5SWarner Losh 727c8befdd5SWarner Losh 728c8befdd5SWarner Losh if (status & STE_ISR_HOSTERR) { 729c8befdd5SWarner Losh ste_reset(sc); 730c8befdd5SWarner Losh ste_init_locked(sc); 731c8befdd5SWarner Losh } 732c8befdd5SWarner Losh } 733c8befdd5SWarner Losh 734c8befdd5SWarner Losh /* Re-enable interrupts */ 735c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 736c8befdd5SWarner Losh 737c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 738c8befdd5SWarner Losh ste_start_locked(ifp); 739c8befdd5SWarner Losh 740c8befdd5SWarner Losh STE_UNLOCK(sc); 741c8befdd5SWarner Losh 742c8befdd5SWarner Losh return; 743c8befdd5SWarner Losh } 744c8befdd5SWarner Losh 745c8befdd5SWarner Losh static void 746c8befdd5SWarner Losh ste_rxeoc(struct ste_softc *sc) 747c8befdd5SWarner Losh { 748c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx; 749c8befdd5SWarner Losh 750c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 751c8befdd5SWarner Losh 752c8befdd5SWarner Losh if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) { 753c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head; 754c8befdd5SWarner Losh do { 755c8befdd5SWarner Losh cur_rx = cur_rx->ste_next; 756c8befdd5SWarner Losh /* If the ring is empty, just return. */ 757c8befdd5SWarner Losh if (cur_rx == sc->ste_cdata.ste_rx_head) 758c8befdd5SWarner Losh return; 759c8befdd5SWarner Losh } while (cur_rx->ste_ptr->ste_status == 0); 760c8befdd5SWarner Losh if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) { 761c8befdd5SWarner Losh /* We've fallen behind the chip: catch it. */ 762c8befdd5SWarner Losh sc->ste_cdata.ste_rx_head = cur_rx; 763c8befdd5SWarner Losh ++ste_rxsyncs; 764c8befdd5SWarner Losh } 765c8befdd5SWarner Losh } 766c8befdd5SWarner Losh } 767c8befdd5SWarner Losh 768c8befdd5SWarner Losh /* 769c8befdd5SWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 770c8befdd5SWarner Losh * the higher level protocols. 771c8befdd5SWarner Losh */ 7721abcdbd1SAttilio Rao static int 773c8befdd5SWarner Losh ste_rxeof(sc) 774c8befdd5SWarner Losh struct ste_softc *sc; 775c8befdd5SWarner Losh { 776c8befdd5SWarner Losh struct mbuf *m; 777c8befdd5SWarner Losh struct ifnet *ifp; 778c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx; 7791abcdbd1SAttilio Rao int total_len = 0, count=0, rx_npkts = 0; 780c8befdd5SWarner Losh u_int32_t rxstat; 781c8befdd5SWarner Losh 782c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 783c8befdd5SWarner Losh 784c8befdd5SWarner Losh ifp = sc->ste_ifp; 785c8befdd5SWarner Losh 786c8befdd5SWarner Losh while((rxstat = sc->ste_cdata.ste_rx_head->ste_ptr->ste_status) 787c8befdd5SWarner Losh & STE_RXSTAT_DMADONE) { 788c8befdd5SWarner Losh #ifdef DEVICE_POLLING 789c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 790c8befdd5SWarner Losh if (sc->rxcycles <= 0) 791c8befdd5SWarner Losh break; 792c8befdd5SWarner Losh sc->rxcycles--; 793c8befdd5SWarner Losh } 794c8befdd5SWarner Losh #endif 795c8befdd5SWarner Losh if ((STE_RX_LIST_CNT - count) < 3) { 796c8befdd5SWarner Losh break; 797c8befdd5SWarner Losh } 798c8befdd5SWarner Losh 799c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head; 800c8befdd5SWarner Losh sc->ste_cdata.ste_rx_head = cur_rx->ste_next; 801c8befdd5SWarner Losh 802c8befdd5SWarner Losh /* 803c8befdd5SWarner Losh * If an error occurs, update stats, clear the 804c8befdd5SWarner Losh * status word and leave the mbuf cluster in place: 805c8befdd5SWarner Losh * it should simply get re-used next time this descriptor 806c8befdd5SWarner Losh * comes up in the ring. 807c8befdd5SWarner Losh */ 808c8befdd5SWarner Losh if (rxstat & STE_RXSTAT_FRAME_ERR) { 809c8befdd5SWarner Losh ifp->if_ierrors++; 810c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 811c8befdd5SWarner Losh continue; 812c8befdd5SWarner Losh } 813c8befdd5SWarner Losh 814c8befdd5SWarner Losh /* 815c8befdd5SWarner Losh * If there error bit was not set, the upload complete 816c8befdd5SWarner Losh * bit should be set which means we have a valid packet. 817c8befdd5SWarner Losh * If not, something truly strange has happened. 818c8befdd5SWarner Losh */ 819c8befdd5SWarner Losh if (!(rxstat & STE_RXSTAT_DMADONE)) { 820c8befdd5SWarner Losh device_printf(sc->ste_dev, 821c8befdd5SWarner Losh "bad receive status -- packet dropped\n"); 822c8befdd5SWarner Losh ifp->if_ierrors++; 823c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 824c8befdd5SWarner Losh continue; 825c8befdd5SWarner Losh } 826c8befdd5SWarner Losh 827c8befdd5SWarner Losh /* No errors; receive the packet. */ 828c8befdd5SWarner Losh m = cur_rx->ste_mbuf; 829c8befdd5SWarner Losh total_len = cur_rx->ste_ptr->ste_status & STE_RXSTAT_FRAMELEN; 830c8befdd5SWarner Losh 831c8befdd5SWarner Losh /* 832c8befdd5SWarner Losh * Try to conjure up a new mbuf cluster. If that 833c8befdd5SWarner Losh * fails, it means we have an out of memory condition and 834c8befdd5SWarner Losh * should leave the buffer in place and continue. This will 835c8befdd5SWarner Losh * result in a lost packet, but there's little else we 836c8befdd5SWarner Losh * can do in this situation. 837c8befdd5SWarner Losh */ 838c8befdd5SWarner Losh if (ste_newbuf(sc, cur_rx, NULL) == ENOBUFS) { 839c8befdd5SWarner Losh ifp->if_ierrors++; 840c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 841c8befdd5SWarner Losh continue; 842c8befdd5SWarner Losh } 843c8befdd5SWarner Losh 844c8befdd5SWarner Losh m->m_pkthdr.rcvif = ifp; 845c8befdd5SWarner Losh m->m_pkthdr.len = m->m_len = total_len; 846c8befdd5SWarner Losh 847c8befdd5SWarner Losh ifp->if_ipackets++; 848c8befdd5SWarner Losh STE_UNLOCK(sc); 849c8befdd5SWarner Losh (*ifp->if_input)(ifp, m); 850c8befdd5SWarner Losh STE_LOCK(sc); 851c8befdd5SWarner Losh 852c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 853c8befdd5SWarner Losh count++; 8541abcdbd1SAttilio Rao rx_npkts++; 855c8befdd5SWarner Losh } 856c8befdd5SWarner Losh 8571abcdbd1SAttilio Rao return (rx_npkts); 858c8befdd5SWarner Losh } 859c8befdd5SWarner Losh 860c8befdd5SWarner Losh static void 861c8befdd5SWarner Losh ste_txeoc(sc) 862c8befdd5SWarner Losh struct ste_softc *sc; 863c8befdd5SWarner Losh { 864c8befdd5SWarner Losh u_int8_t txstat; 865c8befdd5SWarner Losh struct ifnet *ifp; 866c8befdd5SWarner Losh 867c8befdd5SWarner Losh ifp = sc->ste_ifp; 868c8befdd5SWarner Losh 869c8befdd5SWarner Losh while ((txstat = CSR_READ_1(sc, STE_TX_STATUS)) & 870c8befdd5SWarner Losh STE_TXSTATUS_TXDONE) { 871c8befdd5SWarner Losh if (txstat & STE_TXSTATUS_UNDERRUN || 872c8befdd5SWarner Losh txstat & STE_TXSTATUS_EXCESSCOLLS || 873c8befdd5SWarner Losh txstat & STE_TXSTATUS_RECLAIMERR) { 874c8befdd5SWarner Losh ifp->if_oerrors++; 875c8befdd5SWarner Losh device_printf(sc->ste_dev, 876c8befdd5SWarner Losh "transmission error: %x\n", txstat); 877c8befdd5SWarner Losh 878c8befdd5SWarner Losh ste_reset(sc); 879c8befdd5SWarner Losh ste_init_locked(sc); 880c8befdd5SWarner Losh 881c8befdd5SWarner Losh if (txstat & STE_TXSTATUS_UNDERRUN && 882c8befdd5SWarner Losh sc->ste_tx_thresh < STE_PACKET_SIZE) { 883c8befdd5SWarner Losh sc->ste_tx_thresh += STE_MIN_FRAMELEN; 884c8befdd5SWarner Losh device_printf(sc->ste_dev, 885c8befdd5SWarner Losh "tx underrun, increasing tx" 886c8befdd5SWarner Losh " start threshold to %d bytes\n", 887c8befdd5SWarner Losh sc->ste_tx_thresh); 888c8befdd5SWarner Losh } 889c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 890c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_RECLAIM_THRESH, 891c8befdd5SWarner Losh (STE_PACKET_SIZE >> 4)); 892c8befdd5SWarner Losh } 893c8befdd5SWarner Losh ste_init_locked(sc); 894c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STATUS, txstat); 895c8befdd5SWarner Losh } 896c8befdd5SWarner Losh 897c8befdd5SWarner Losh return; 898c8befdd5SWarner Losh } 899c8befdd5SWarner Losh 900c8befdd5SWarner Losh static void 901c8befdd5SWarner Losh ste_txeof(sc) 902c8befdd5SWarner Losh struct ste_softc *sc; 903c8befdd5SWarner Losh { 904c8befdd5SWarner Losh struct ste_chain *cur_tx; 905c8befdd5SWarner Losh struct ifnet *ifp; 906c8befdd5SWarner Losh int idx; 907c8befdd5SWarner Losh 908c8befdd5SWarner Losh ifp = sc->ste_ifp; 909c8befdd5SWarner Losh 910c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_cons; 911c8befdd5SWarner Losh while(idx != sc->ste_cdata.ste_tx_prod) { 912c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; 913c8befdd5SWarner Losh 914c8befdd5SWarner Losh if (!(cur_tx->ste_ptr->ste_ctl & STE_TXCTL_DMADONE)) 915c8befdd5SWarner Losh break; 916c8befdd5SWarner Losh 917c8befdd5SWarner Losh m_freem(cur_tx->ste_mbuf); 918c8befdd5SWarner Losh cur_tx->ste_mbuf = NULL; 919c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 920c8befdd5SWarner Losh ifp->if_opackets++; 921c8befdd5SWarner Losh 922c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT); 923c8befdd5SWarner Losh } 924c8befdd5SWarner Losh 925c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons = idx; 926c8befdd5SWarner Losh if (idx == sc->ste_cdata.ste_tx_prod) 927c8befdd5SWarner Losh ifp->if_timer = 0; 928c8befdd5SWarner Losh } 929c8befdd5SWarner Losh 930c8befdd5SWarner Losh static void 931c8befdd5SWarner Losh ste_stats_update(xsc) 932c8befdd5SWarner Losh void *xsc; 933c8befdd5SWarner Losh { 934c8befdd5SWarner Losh struct ste_softc *sc; 935c8befdd5SWarner Losh struct ifnet *ifp; 936c8befdd5SWarner Losh struct mii_data *mii; 937c8befdd5SWarner Losh 938c8befdd5SWarner Losh sc = xsc; 939c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 940c8befdd5SWarner Losh 941c8befdd5SWarner Losh ifp = sc->ste_ifp; 942c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 943c8befdd5SWarner Losh 944c8befdd5SWarner Losh ifp->if_collisions += CSR_READ_1(sc, STE_LATE_COLLS) 945c8befdd5SWarner Losh + CSR_READ_1(sc, STE_MULTI_COLLS) 946c8befdd5SWarner Losh + CSR_READ_1(sc, STE_SINGLE_COLLS); 947c8befdd5SWarner Losh 948c8befdd5SWarner Losh if (!sc->ste_link) { 949c8befdd5SWarner Losh mii_pollstat(mii); 950c8befdd5SWarner Losh if (mii->mii_media_status & IFM_ACTIVE && 951c8befdd5SWarner Losh IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 952c8befdd5SWarner Losh sc->ste_link++; 953c8befdd5SWarner Losh /* 954c8befdd5SWarner Losh * we don't get a call-back on re-init so do it 955c8befdd5SWarner Losh * otherwise we get stuck in the wrong link state 956c8befdd5SWarner Losh */ 957c8befdd5SWarner Losh ste_miibus_statchg(sc->ste_dev); 958c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 959c8befdd5SWarner Losh ste_start_locked(ifp); 960c8befdd5SWarner Losh } 961c8befdd5SWarner Losh } 962c8befdd5SWarner Losh 963c8befdd5SWarner Losh callout_reset(&sc->ste_stat_callout, hz, ste_stats_update, sc); 964c8befdd5SWarner Losh 965c8befdd5SWarner Losh return; 966c8befdd5SWarner Losh } 967c8befdd5SWarner Losh 968c8befdd5SWarner Losh 969c8befdd5SWarner Losh /* 970c8befdd5SWarner Losh * Probe for a Sundance ST201 chip. Check the PCI vendor and device 971c8befdd5SWarner Losh * IDs against our list and return a device name if we find a match. 972c8befdd5SWarner Losh */ 973c8befdd5SWarner Losh static int 974c8befdd5SWarner Losh ste_probe(dev) 975c8befdd5SWarner Losh device_t dev; 976c8befdd5SWarner Losh { 977c8befdd5SWarner Losh struct ste_type *t; 978c8befdd5SWarner Losh 979c8befdd5SWarner Losh t = ste_devs; 980c8befdd5SWarner Losh 981c8befdd5SWarner Losh while(t->ste_name != NULL) { 982c8befdd5SWarner Losh if ((pci_get_vendor(dev) == t->ste_vid) && 983c8befdd5SWarner Losh (pci_get_device(dev) == t->ste_did)) { 984c8befdd5SWarner Losh device_set_desc(dev, t->ste_name); 985c8befdd5SWarner Losh return (BUS_PROBE_DEFAULT); 986c8befdd5SWarner Losh } 987c8befdd5SWarner Losh t++; 988c8befdd5SWarner Losh } 989c8befdd5SWarner Losh 990c8befdd5SWarner Losh return(ENXIO); 991c8befdd5SWarner Losh } 992c8befdd5SWarner Losh 993c8befdd5SWarner Losh /* 994c8befdd5SWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 995c8befdd5SWarner Losh * setup and ethernet/BPF attach. 996c8befdd5SWarner Losh */ 997c8befdd5SWarner Losh static int 998c8befdd5SWarner Losh ste_attach(dev) 999c8befdd5SWarner Losh device_t dev; 1000c8befdd5SWarner Losh { 1001c8befdd5SWarner Losh struct ste_softc *sc; 1002c8befdd5SWarner Losh struct ifnet *ifp; 1003c8befdd5SWarner Losh int error = 0, rid; 1004c8befdd5SWarner Losh u_char eaddr[6]; 1005c8befdd5SWarner Losh 1006c8befdd5SWarner Losh sc = device_get_softc(dev); 1007c8befdd5SWarner Losh sc->ste_dev = dev; 1008c8befdd5SWarner Losh 1009c8befdd5SWarner Losh /* 1010c8befdd5SWarner Losh * Only use one PHY since this chip reports multiple 1011c8befdd5SWarner Losh * Note on the DFE-550 the PHY is at 1 on the DFE-580 1012c8befdd5SWarner Losh * it is at 0 & 1. It is rev 0x12. 1013c8befdd5SWarner Losh */ 1014c8befdd5SWarner Losh if (pci_get_vendor(dev) == DL_VENDORID && 1015c8befdd5SWarner Losh pci_get_device(dev) == DL_DEVICEID_DL10050 && 1016c8befdd5SWarner Losh pci_get_revid(dev) == 0x12 ) 1017c8befdd5SWarner Losh sc->ste_one_phy = 1; 1018c8befdd5SWarner Losh 1019c8befdd5SWarner Losh mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 1020c8befdd5SWarner Losh MTX_DEF); 1021c8befdd5SWarner Losh /* 1022c8befdd5SWarner Losh * Map control/status registers. 1023c8befdd5SWarner Losh */ 1024c8befdd5SWarner Losh pci_enable_busmaster(dev); 1025c8befdd5SWarner Losh 1026c8befdd5SWarner Losh rid = STE_RID; 1027c8befdd5SWarner Losh sc->ste_res = bus_alloc_resource_any(dev, STE_RES, &rid, RF_ACTIVE); 1028c8befdd5SWarner Losh 1029c8befdd5SWarner Losh if (sc->ste_res == NULL) { 1030c8befdd5SWarner Losh device_printf(dev, "couldn't map ports/memory\n"); 1031c8befdd5SWarner Losh error = ENXIO; 1032c8befdd5SWarner Losh goto fail; 1033c8befdd5SWarner Losh } 1034c8befdd5SWarner Losh 1035c8befdd5SWarner Losh sc->ste_btag = rman_get_bustag(sc->ste_res); 1036c8befdd5SWarner Losh sc->ste_bhandle = rman_get_bushandle(sc->ste_res); 1037c8befdd5SWarner Losh 1038c8befdd5SWarner Losh /* Allocate interrupt */ 1039c8befdd5SWarner Losh rid = 0; 1040c8befdd5SWarner Losh sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 1041c8befdd5SWarner Losh RF_SHAREABLE | RF_ACTIVE); 1042c8befdd5SWarner Losh 1043c8befdd5SWarner Losh if (sc->ste_irq == NULL) { 1044c8befdd5SWarner Losh device_printf(dev, "couldn't map interrupt\n"); 1045c8befdd5SWarner Losh error = ENXIO; 1046c8befdd5SWarner Losh goto fail; 1047c8befdd5SWarner Losh } 1048c8befdd5SWarner Losh 1049c8befdd5SWarner Losh callout_init_mtx(&sc->ste_stat_callout, &sc->ste_mtx, 0); 1050c8befdd5SWarner Losh 1051c8befdd5SWarner Losh /* Reset the adapter. */ 1052c8befdd5SWarner Losh ste_reset(sc); 1053c8befdd5SWarner Losh 1054c8befdd5SWarner Losh /* 1055c8befdd5SWarner Losh * Get station address from the EEPROM. 1056c8befdd5SWarner Losh */ 1057c8befdd5SWarner Losh if (ste_read_eeprom(sc, eaddr, 1058c8befdd5SWarner Losh STE_EEADDR_NODE0, 3, 0)) { 1059c8befdd5SWarner Losh device_printf(dev, "failed to read station address\n"); 1060c8befdd5SWarner Losh error = ENXIO;; 1061c8befdd5SWarner Losh goto fail; 1062c8befdd5SWarner Losh } 1063c8befdd5SWarner Losh 1064c8befdd5SWarner Losh /* Allocate the descriptor queues. */ 1065c8befdd5SWarner Losh sc->ste_ldata = contigmalloc(sizeof(struct ste_list_data), M_DEVBUF, 1066c8befdd5SWarner Losh M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 1067c8befdd5SWarner Losh 1068c8befdd5SWarner Losh if (sc->ste_ldata == NULL) { 1069c8befdd5SWarner Losh device_printf(dev, "no memory for list buffers!\n"); 1070c8befdd5SWarner Losh error = ENXIO; 1071c8befdd5SWarner Losh goto fail; 1072c8befdd5SWarner Losh } 1073c8befdd5SWarner Losh 1074c8befdd5SWarner Losh bzero(sc->ste_ldata, sizeof(struct ste_list_data)); 1075c8befdd5SWarner Losh 1076c8befdd5SWarner Losh ifp = sc->ste_ifp = if_alloc(IFT_ETHER); 1077c8befdd5SWarner Losh if (ifp == NULL) { 1078c8befdd5SWarner Losh device_printf(dev, "can not if_alloc()\n"); 1079c8befdd5SWarner Losh error = ENOSPC; 1080c8befdd5SWarner Losh goto fail; 1081c8befdd5SWarner Losh } 1082c8befdd5SWarner Losh 1083c8befdd5SWarner Losh /* Do MII setup. */ 1084c8befdd5SWarner Losh if (mii_phy_probe(dev, &sc->ste_miibus, 1085c8befdd5SWarner Losh ste_ifmedia_upd, ste_ifmedia_sts)) { 1086c8befdd5SWarner Losh device_printf(dev, "MII without any phy!\n"); 1087c8befdd5SWarner Losh error = ENXIO; 1088c8befdd5SWarner Losh goto fail; 1089c8befdd5SWarner Losh } 1090c8befdd5SWarner Losh 1091c8befdd5SWarner Losh ifp->if_softc = sc; 1092c8befdd5SWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1093c8befdd5SWarner Losh ifp->if_mtu = ETHERMTU; 1094c8befdd5SWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1095c8befdd5SWarner Losh ifp->if_ioctl = ste_ioctl; 1096c8befdd5SWarner Losh ifp->if_start = ste_start; 1097c8befdd5SWarner Losh ifp->if_watchdog = ste_watchdog; 1098c8befdd5SWarner Losh ifp->if_init = ste_init; 1099c8befdd5SWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, STE_TX_LIST_CNT - 1); 1100c8befdd5SWarner Losh ifp->if_snd.ifq_drv_maxlen = STE_TX_LIST_CNT - 1; 1101c8befdd5SWarner Losh IFQ_SET_READY(&ifp->if_snd); 1102c8befdd5SWarner Losh 1103c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1104c8befdd5SWarner Losh 1105c8befdd5SWarner Losh /* 1106c8befdd5SWarner Losh * Call MI attach routine. 1107c8befdd5SWarner Losh */ 1108c8befdd5SWarner Losh ether_ifattach(ifp, eaddr); 1109c8befdd5SWarner Losh 1110c8befdd5SWarner Losh /* 1111c8befdd5SWarner Losh * Tell the upper layer(s) we support long frames. 1112c8befdd5SWarner Losh */ 1113c8befdd5SWarner Losh ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 1114c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 1115c8befdd5SWarner Losh ifp->if_capenable = ifp->if_capabilities; 1116c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1117c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1118c8befdd5SWarner Losh #endif 1119c8befdd5SWarner Losh 1120c8befdd5SWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1121c8befdd5SWarner Losh error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET | INTR_MPSAFE, 1122c8befdd5SWarner Losh NULL, ste_intr, sc, &sc->ste_intrhand); 1123c8befdd5SWarner Losh 1124c8befdd5SWarner Losh if (error) { 1125c8befdd5SWarner Losh device_printf(dev, "couldn't set up irq\n"); 1126c8befdd5SWarner Losh ether_ifdetach(ifp); 1127c8befdd5SWarner Losh goto fail; 1128c8befdd5SWarner Losh } 1129c8befdd5SWarner Losh 1130c8befdd5SWarner Losh fail: 1131c8befdd5SWarner Losh if (error) 1132c8befdd5SWarner Losh ste_detach(dev); 1133c8befdd5SWarner Losh 1134c8befdd5SWarner Losh return(error); 1135c8befdd5SWarner Losh } 1136c8befdd5SWarner Losh 1137c8befdd5SWarner Losh /* 1138c8befdd5SWarner Losh * Shutdown hardware and free up resources. This can be called any 1139c8befdd5SWarner Losh * time after the mutex has been initialized. It is called in both 1140c8befdd5SWarner Losh * the error case in attach and the normal detach case so it needs 1141c8befdd5SWarner Losh * to be careful about only freeing resources that have actually been 1142c8befdd5SWarner Losh * allocated. 1143c8befdd5SWarner Losh */ 1144c8befdd5SWarner Losh static int 1145c8befdd5SWarner Losh ste_detach(dev) 1146c8befdd5SWarner Losh device_t dev; 1147c8befdd5SWarner Losh { 1148c8befdd5SWarner Losh struct ste_softc *sc; 1149c8befdd5SWarner Losh struct ifnet *ifp; 1150c8befdd5SWarner Losh 1151c8befdd5SWarner Losh sc = device_get_softc(dev); 1152c8befdd5SWarner Losh KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized")); 1153c8befdd5SWarner Losh ifp = sc->ste_ifp; 1154c8befdd5SWarner Losh 1155c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1156c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1157c8befdd5SWarner Losh ether_poll_deregister(ifp); 1158c8befdd5SWarner Losh #endif 1159c8befdd5SWarner Losh 1160c8befdd5SWarner Losh /* These should only be active if attach succeeded */ 1161c8befdd5SWarner Losh if (device_is_attached(dev)) { 1162c8befdd5SWarner Losh STE_LOCK(sc); 1163c8befdd5SWarner Losh ste_stop(sc); 1164c8befdd5SWarner Losh STE_UNLOCK(sc); 1165c8befdd5SWarner Losh callout_drain(&sc->ste_stat_callout); 1166c8befdd5SWarner Losh ether_ifdetach(ifp); 1167c8befdd5SWarner Losh } 1168c8befdd5SWarner Losh if (sc->ste_miibus) 1169c8befdd5SWarner Losh device_delete_child(dev, sc->ste_miibus); 1170c8befdd5SWarner Losh bus_generic_detach(dev); 1171c8befdd5SWarner Losh 1172c8befdd5SWarner Losh if (sc->ste_intrhand) 1173c8befdd5SWarner Losh bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); 1174c8befdd5SWarner Losh if (sc->ste_irq) 1175c8befdd5SWarner Losh bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); 1176c8befdd5SWarner Losh if (sc->ste_res) 1177c8befdd5SWarner Losh bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); 1178c8befdd5SWarner Losh 1179c8befdd5SWarner Losh if (ifp) 1180c8befdd5SWarner Losh if_free(ifp); 1181c8befdd5SWarner Losh 1182c8befdd5SWarner Losh if (sc->ste_ldata) { 1183c8befdd5SWarner Losh contigfree(sc->ste_ldata, sizeof(struct ste_list_data), 1184c8befdd5SWarner Losh M_DEVBUF); 1185c8befdd5SWarner Losh } 1186c8befdd5SWarner Losh 1187c8befdd5SWarner Losh mtx_destroy(&sc->ste_mtx); 1188c8befdd5SWarner Losh 1189c8befdd5SWarner Losh return(0); 1190c8befdd5SWarner Losh } 1191c8befdd5SWarner Losh 1192c8befdd5SWarner Losh static int 1193c8befdd5SWarner Losh ste_newbuf(sc, c, m) 1194c8befdd5SWarner Losh struct ste_softc *sc; 1195c8befdd5SWarner Losh struct ste_chain_onefrag *c; 1196c8befdd5SWarner Losh struct mbuf *m; 1197c8befdd5SWarner Losh { 1198c8befdd5SWarner Losh struct mbuf *m_new = NULL; 1199c8befdd5SWarner Losh 1200c8befdd5SWarner Losh if (m == NULL) { 1201c8befdd5SWarner Losh MGETHDR(m_new, M_DONTWAIT, MT_DATA); 1202c8befdd5SWarner Losh if (m_new == NULL) 1203c8befdd5SWarner Losh return(ENOBUFS); 1204c8befdd5SWarner Losh MCLGET(m_new, M_DONTWAIT); 1205c8befdd5SWarner Losh if (!(m_new->m_flags & M_EXT)) { 1206c8befdd5SWarner Losh m_freem(m_new); 1207c8befdd5SWarner Losh return(ENOBUFS); 1208c8befdd5SWarner Losh } 1209c8befdd5SWarner Losh m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 1210c8befdd5SWarner Losh } else { 1211c8befdd5SWarner Losh m_new = m; 1212c8befdd5SWarner Losh m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 1213c8befdd5SWarner Losh m_new->m_data = m_new->m_ext.ext_buf; 1214c8befdd5SWarner Losh } 1215c8befdd5SWarner Losh 1216c8befdd5SWarner Losh m_adj(m_new, ETHER_ALIGN); 1217c8befdd5SWarner Losh 1218c8befdd5SWarner Losh c->ste_mbuf = m_new; 1219c8befdd5SWarner Losh c->ste_ptr->ste_status = 0; 1220c8befdd5SWarner Losh c->ste_ptr->ste_frag.ste_addr = vtophys(mtod(m_new, caddr_t)); 1221c8befdd5SWarner Losh c->ste_ptr->ste_frag.ste_len = (1536 + ETHER_VLAN_ENCAP_LEN) | STE_FRAG_LAST; 1222c8befdd5SWarner Losh 1223c8befdd5SWarner Losh return(0); 1224c8befdd5SWarner Losh } 1225c8befdd5SWarner Losh 1226c8befdd5SWarner Losh static int 1227c8befdd5SWarner Losh ste_init_rx_list(sc) 1228c8befdd5SWarner Losh struct ste_softc *sc; 1229c8befdd5SWarner Losh { 1230c8befdd5SWarner Losh struct ste_chain_data *cd; 1231c8befdd5SWarner Losh struct ste_list_data *ld; 1232c8befdd5SWarner Losh int i; 1233c8befdd5SWarner Losh 1234c8befdd5SWarner Losh cd = &sc->ste_cdata; 1235c8befdd5SWarner Losh ld = sc->ste_ldata; 1236c8befdd5SWarner Losh 1237c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1238c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; 1239c8befdd5SWarner Losh if (ste_newbuf(sc, &cd->ste_rx_chain[i], NULL) == ENOBUFS) 1240c8befdd5SWarner Losh return(ENOBUFS); 1241c8befdd5SWarner Losh if (i == (STE_RX_LIST_CNT - 1)) { 1242c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_next = 1243c8befdd5SWarner Losh &cd->ste_rx_chain[0]; 1244c8befdd5SWarner Losh ld->ste_rx_list[i].ste_next = 1245c8befdd5SWarner Losh vtophys(&ld->ste_rx_list[0]); 1246c8befdd5SWarner Losh } else { 1247c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_next = 1248c8befdd5SWarner Losh &cd->ste_rx_chain[i + 1]; 1249c8befdd5SWarner Losh ld->ste_rx_list[i].ste_next = 1250c8befdd5SWarner Losh vtophys(&ld->ste_rx_list[i + 1]); 1251c8befdd5SWarner Losh } 1252c8befdd5SWarner Losh ld->ste_rx_list[i].ste_status = 0; 1253c8befdd5SWarner Losh } 1254c8befdd5SWarner Losh 1255c8befdd5SWarner Losh cd->ste_rx_head = &cd->ste_rx_chain[0]; 1256c8befdd5SWarner Losh 1257c8befdd5SWarner Losh return(0); 1258c8befdd5SWarner Losh } 1259c8befdd5SWarner Losh 1260c8befdd5SWarner Losh static void 1261c8befdd5SWarner Losh ste_init_tx_list(sc) 1262c8befdd5SWarner Losh struct ste_softc *sc; 1263c8befdd5SWarner Losh { 1264c8befdd5SWarner Losh struct ste_chain_data *cd; 1265c8befdd5SWarner Losh struct ste_list_data *ld; 1266c8befdd5SWarner Losh int i; 1267c8befdd5SWarner Losh 1268c8befdd5SWarner Losh cd = &sc->ste_cdata; 1269c8befdd5SWarner Losh ld = sc->ste_ldata; 1270c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1271c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; 1272c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr->ste_next = 0; 1273c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr->ste_ctl = 0; 1274c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_phys = vtophys(&ld->ste_tx_list[i]); 1275c8befdd5SWarner Losh if (i == (STE_TX_LIST_CNT - 1)) 1276c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_next = 1277c8befdd5SWarner Losh &cd->ste_tx_chain[0]; 1278c8befdd5SWarner Losh else 1279c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_next = 1280c8befdd5SWarner Losh &cd->ste_tx_chain[i + 1]; 1281c8befdd5SWarner Losh } 1282c8befdd5SWarner Losh 1283c8befdd5SWarner Losh cd->ste_tx_prod = 0; 1284c8befdd5SWarner Losh cd->ste_tx_cons = 0; 1285c8befdd5SWarner Losh 1286c8befdd5SWarner Losh return; 1287c8befdd5SWarner Losh } 1288c8befdd5SWarner Losh 1289c8befdd5SWarner Losh static void 1290c8befdd5SWarner Losh ste_init(xsc) 1291c8befdd5SWarner Losh void *xsc; 1292c8befdd5SWarner Losh { 1293c8befdd5SWarner Losh struct ste_softc *sc; 1294c8befdd5SWarner Losh 1295c8befdd5SWarner Losh sc = xsc; 1296c8befdd5SWarner Losh STE_LOCK(sc); 1297c8befdd5SWarner Losh ste_init_locked(sc); 1298c8befdd5SWarner Losh STE_UNLOCK(sc); 1299c8befdd5SWarner Losh } 1300c8befdd5SWarner Losh 1301c8befdd5SWarner Losh static void 1302c8befdd5SWarner Losh ste_init_locked(sc) 1303c8befdd5SWarner Losh struct ste_softc *sc; 1304c8befdd5SWarner Losh { 1305c8befdd5SWarner Losh int i; 1306c8befdd5SWarner Losh struct ifnet *ifp; 1307c8befdd5SWarner Losh 1308c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1309c8befdd5SWarner Losh ifp = sc->ste_ifp; 1310c8befdd5SWarner Losh 1311c8befdd5SWarner Losh ste_stop(sc); 1312c8befdd5SWarner Losh 1313c8befdd5SWarner Losh /* Init our MAC address */ 1314c8befdd5SWarner Losh for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 1315c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PAR0 + i, 1316c8befdd5SWarner Losh ((IF_LLADDR(sc->ste_ifp)[i] & 0xff) | 1317c8befdd5SWarner Losh IF_LLADDR(sc->ste_ifp)[i + 1] << 8)); 1318c8befdd5SWarner Losh } 1319c8befdd5SWarner Losh 1320c8befdd5SWarner Losh /* Init RX list */ 1321c8befdd5SWarner Losh if (ste_init_rx_list(sc) == ENOBUFS) { 1322c8befdd5SWarner Losh device_printf(sc->ste_dev, 1323c8befdd5SWarner Losh "initialization failed: no memory for RX buffers\n"); 1324c8befdd5SWarner Losh ste_stop(sc); 1325c8befdd5SWarner Losh return; 1326c8befdd5SWarner Losh } 1327c8befdd5SWarner Losh 1328c8befdd5SWarner Losh /* Set RX polling interval */ 1329c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); 1330c8befdd5SWarner Losh 1331c8befdd5SWarner Losh /* Init TX descriptors */ 1332c8befdd5SWarner Losh ste_init_tx_list(sc); 1333c8befdd5SWarner Losh 1334c8befdd5SWarner Losh /* Set the TX freethresh value */ 1335c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); 1336c8befdd5SWarner Losh 1337c8befdd5SWarner Losh /* Set the TX start threshold for best performance. */ 1338c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 1339c8befdd5SWarner Losh 1340c8befdd5SWarner Losh /* Set the TX reclaim threshold. */ 1341c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); 1342c8befdd5SWarner Losh 1343c8befdd5SWarner Losh /* Set up the RX filter. */ 1344c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_MODE, STE_RXMODE_UNICAST); 1345c8befdd5SWarner Losh 1346c8befdd5SWarner Losh /* If we want promiscuous mode, set the allframes bit. */ 1347c8befdd5SWarner Losh if (ifp->if_flags & IFF_PROMISC) { 1348c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); 1349c8befdd5SWarner Losh } else { 1350c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); 1351c8befdd5SWarner Losh } 1352c8befdd5SWarner Losh 1353c8befdd5SWarner Losh /* Set capture broadcast bit to accept broadcast frames. */ 1354c8befdd5SWarner Losh if (ifp->if_flags & IFF_BROADCAST) { 1355c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); 1356c8befdd5SWarner Losh } else { 1357c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); 1358c8befdd5SWarner Losh } 1359c8befdd5SWarner Losh 1360c8befdd5SWarner Losh ste_setmulti(sc); 1361c8befdd5SWarner Losh 1362c8befdd5SWarner Losh /* Load the address of the RX list. */ 1363c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1364c8befdd5SWarner Losh ste_wait(sc); 1365c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 1366c8befdd5SWarner Losh vtophys(&sc->ste_ldata->ste_rx_list[0])); 1367c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1368c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1369c8befdd5SWarner Losh 1370c8befdd5SWarner Losh /* Set TX polling interval (defer until we TX first packet */ 1371c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 1372c8befdd5SWarner Losh 1373c8befdd5SWarner Losh /* Load address of the TX list */ 1374c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1375c8befdd5SWarner Losh ste_wait(sc); 1376c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 1377c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1378c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1379c8befdd5SWarner Losh ste_wait(sc); 1380c8befdd5SWarner Losh sc->ste_tx_prev = NULL; 1381c8befdd5SWarner Losh 1382c8befdd5SWarner Losh /* Enable receiver and transmitter */ 1383c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL0, 0); 1384c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL1, 0); 1385c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); 1386c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); 1387c8befdd5SWarner Losh 1388c8befdd5SWarner Losh /* Enable stats counters. */ 1389c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); 1390c8befdd5SWarner Losh 1391c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_ISR, 0xFFFF); 1392c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1393c8befdd5SWarner Losh /* Disable interrupts if we are polling. */ 1394c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1395c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1396c8befdd5SWarner Losh else 1397c8befdd5SWarner Losh #endif 1398c8befdd5SWarner Losh /* Enable interrupts. */ 1399c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1400c8befdd5SWarner Losh 1401c8befdd5SWarner Losh /* Accept VLAN length packets */ 1402c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN); 1403c8befdd5SWarner Losh 1404c8befdd5SWarner Losh ste_ifmedia_upd_locked(ifp); 1405c8befdd5SWarner Losh 1406c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 1407c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1408c8befdd5SWarner Losh 1409c8befdd5SWarner Losh callout_reset(&sc->ste_stat_callout, hz, ste_stats_update, sc); 1410c8befdd5SWarner Losh 1411c8befdd5SWarner Losh return; 1412c8befdd5SWarner Losh } 1413c8befdd5SWarner Losh 1414c8befdd5SWarner Losh static void 1415c8befdd5SWarner Losh ste_stop(sc) 1416c8befdd5SWarner Losh struct ste_softc *sc; 1417c8befdd5SWarner Losh { 1418c8befdd5SWarner Losh int i; 1419c8befdd5SWarner Losh struct ifnet *ifp; 1420c8befdd5SWarner Losh 1421c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1422c8befdd5SWarner Losh ifp = sc->ste_ifp; 1423c8befdd5SWarner Losh 1424c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 1425c8befdd5SWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE); 1426c8befdd5SWarner Losh 1427c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1428c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_DISABLE); 1429c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_DISABLE); 1430c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_DISABLE); 1431c8befdd5SWarner Losh STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1432c8befdd5SWarner Losh STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1433c8befdd5SWarner Losh ste_wait(sc); 1434c8befdd5SWarner Losh /* 1435c8befdd5SWarner Losh * Try really hard to stop the RX engine or under heavy RX 1436c8befdd5SWarner Losh * data chip will write into de-allocated memory. 1437c8befdd5SWarner Losh */ 1438c8befdd5SWarner Losh ste_reset(sc); 1439c8befdd5SWarner Losh 1440c8befdd5SWarner Losh sc->ste_link = 0; 1441c8befdd5SWarner Losh 1442c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1443c8befdd5SWarner Losh if (sc->ste_cdata.ste_rx_chain[i].ste_mbuf != NULL) { 1444c8befdd5SWarner Losh m_freem(sc->ste_cdata.ste_rx_chain[i].ste_mbuf); 1445c8befdd5SWarner Losh sc->ste_cdata.ste_rx_chain[i].ste_mbuf = NULL; 1446c8befdd5SWarner Losh } 1447c8befdd5SWarner Losh } 1448c8befdd5SWarner Losh 1449c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1450c8befdd5SWarner Losh if (sc->ste_cdata.ste_tx_chain[i].ste_mbuf != NULL) { 1451c8befdd5SWarner Losh m_freem(sc->ste_cdata.ste_tx_chain[i].ste_mbuf); 1452c8befdd5SWarner Losh sc->ste_cdata.ste_tx_chain[i].ste_mbuf = NULL; 1453c8befdd5SWarner Losh } 1454c8befdd5SWarner Losh } 1455c8befdd5SWarner Losh 1456c8befdd5SWarner Losh bzero(sc->ste_ldata, sizeof(struct ste_list_data)); 1457c8befdd5SWarner Losh 1458c8befdd5SWarner Losh return; 1459c8befdd5SWarner Losh } 1460c8befdd5SWarner Losh 1461c8befdd5SWarner Losh static void 1462c8befdd5SWarner Losh ste_reset(sc) 1463c8befdd5SWarner Losh struct ste_softc *sc; 1464c8befdd5SWarner Losh { 1465c8befdd5SWarner Losh int i; 1466c8befdd5SWarner Losh 1467c8befdd5SWarner Losh STE_SETBIT4(sc, STE_ASICCTL, 1468c8befdd5SWarner Losh STE_ASICCTL_GLOBAL_RESET|STE_ASICCTL_RX_RESET| 1469c8befdd5SWarner Losh STE_ASICCTL_TX_RESET|STE_ASICCTL_DMA_RESET| 1470c8befdd5SWarner Losh STE_ASICCTL_FIFO_RESET|STE_ASICCTL_NETWORK_RESET| 1471c8befdd5SWarner Losh STE_ASICCTL_AUTOINIT_RESET|STE_ASICCTL_HOST_RESET| 1472c8befdd5SWarner Losh STE_ASICCTL_EXTRESET_RESET); 1473c8befdd5SWarner Losh 1474c8befdd5SWarner Losh DELAY(100000); 1475c8befdd5SWarner Losh 1476c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 1477c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) 1478c8befdd5SWarner Losh break; 1479c8befdd5SWarner Losh } 1480c8befdd5SWarner Losh 1481c8befdd5SWarner Losh if (i == STE_TIMEOUT) 1482c8befdd5SWarner Losh device_printf(sc->ste_dev, "global reset never completed\n"); 1483c8befdd5SWarner Losh 1484c8befdd5SWarner Losh return; 1485c8befdd5SWarner Losh } 1486c8befdd5SWarner Losh 1487c8befdd5SWarner Losh static int 1488c8befdd5SWarner Losh ste_ioctl(ifp, command, data) 1489c8befdd5SWarner Losh struct ifnet *ifp; 1490c8befdd5SWarner Losh u_long command; 1491c8befdd5SWarner Losh caddr_t data; 1492c8befdd5SWarner Losh { 1493c8befdd5SWarner Losh struct ste_softc *sc; 1494c8befdd5SWarner Losh struct ifreq *ifr; 1495c8befdd5SWarner Losh struct mii_data *mii; 1496c8befdd5SWarner Losh int error = 0; 1497c8befdd5SWarner Losh 1498c8befdd5SWarner Losh sc = ifp->if_softc; 1499c8befdd5SWarner Losh ifr = (struct ifreq *)data; 1500c8befdd5SWarner Losh 1501c8befdd5SWarner Losh switch(command) { 1502c8befdd5SWarner Losh case SIOCSIFFLAGS: 1503c8befdd5SWarner Losh STE_LOCK(sc); 1504c8befdd5SWarner Losh if (ifp->if_flags & IFF_UP) { 1505c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1506c8befdd5SWarner Losh ifp->if_flags & IFF_PROMISC && 1507c8befdd5SWarner Losh !(sc->ste_if_flags & IFF_PROMISC)) { 1508c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, 1509c8befdd5SWarner Losh STE_RXMODE_PROMISC); 1510c8befdd5SWarner Losh } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1511c8befdd5SWarner Losh !(ifp->if_flags & IFF_PROMISC) && 1512c8befdd5SWarner Losh sc->ste_if_flags & IFF_PROMISC) { 1513c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, 1514c8befdd5SWarner Losh STE_RXMODE_PROMISC); 1515c8befdd5SWarner Losh } 1516c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1517c8befdd5SWarner Losh (ifp->if_flags ^ sc->ste_if_flags) & IFF_ALLMULTI) 1518c8befdd5SWarner Losh ste_setmulti(sc); 1519c8befdd5SWarner Losh if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1520c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1521c8befdd5SWarner Losh ste_init_locked(sc); 1522c8befdd5SWarner Losh } 1523c8befdd5SWarner Losh } else { 1524c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1525c8befdd5SWarner Losh ste_stop(sc); 1526c8befdd5SWarner Losh } 1527c8befdd5SWarner Losh sc->ste_if_flags = ifp->if_flags; 1528c8befdd5SWarner Losh STE_UNLOCK(sc); 1529c8befdd5SWarner Losh error = 0; 1530c8befdd5SWarner Losh break; 1531c8befdd5SWarner Losh case SIOCADDMULTI: 1532c8befdd5SWarner Losh case SIOCDELMULTI: 1533c8befdd5SWarner Losh STE_LOCK(sc); 1534c8befdd5SWarner Losh ste_setmulti(sc); 1535c8befdd5SWarner Losh STE_UNLOCK(sc); 1536c8befdd5SWarner Losh error = 0; 1537c8befdd5SWarner Losh break; 1538c8befdd5SWarner Losh case SIOCGIFMEDIA: 1539c8befdd5SWarner Losh case SIOCSIFMEDIA: 1540c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 1541c8befdd5SWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1542c8befdd5SWarner Losh break; 1543c8befdd5SWarner Losh case SIOCSIFCAP: 1544c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1545c8befdd5SWarner Losh if (ifr->ifr_reqcap & IFCAP_POLLING && 1546c8befdd5SWarner Losh !(ifp->if_capenable & IFCAP_POLLING)) { 1547c8befdd5SWarner Losh error = ether_poll_register(ste_poll, ifp); 1548c8befdd5SWarner Losh if (error) 1549c8befdd5SWarner Losh return(error); 1550c8befdd5SWarner Losh STE_LOCK(sc); 1551c8befdd5SWarner Losh /* Disable interrupts */ 1552c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1553c8befdd5SWarner Losh ifp->if_capenable |= IFCAP_POLLING; 1554c8befdd5SWarner Losh STE_UNLOCK(sc); 1555c8befdd5SWarner Losh return (error); 1556c8befdd5SWarner Losh 1557c8befdd5SWarner Losh } 1558c8befdd5SWarner Losh if (!(ifr->ifr_reqcap & IFCAP_POLLING) && 1559c8befdd5SWarner Losh ifp->if_capenable & IFCAP_POLLING) { 1560c8befdd5SWarner Losh error = ether_poll_deregister(ifp); 1561c8befdd5SWarner Losh /* Enable interrupts. */ 1562c8befdd5SWarner Losh STE_LOCK(sc); 1563c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1564c8befdd5SWarner Losh ifp->if_capenable &= ~IFCAP_POLLING; 1565c8befdd5SWarner Losh STE_UNLOCK(sc); 1566c8befdd5SWarner Losh return (error); 1567c8befdd5SWarner Losh } 1568c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 1569c8befdd5SWarner Losh break; 1570c8befdd5SWarner Losh default: 1571c8befdd5SWarner Losh error = ether_ioctl(ifp, command, data); 1572c8befdd5SWarner Losh break; 1573c8befdd5SWarner Losh } 1574c8befdd5SWarner Losh 1575c8befdd5SWarner Losh return(error); 1576c8befdd5SWarner Losh } 1577c8befdd5SWarner Losh 1578c8befdd5SWarner Losh static int 1579c8befdd5SWarner Losh ste_encap(sc, c, m_head) 1580c8befdd5SWarner Losh struct ste_softc *sc; 1581c8befdd5SWarner Losh struct ste_chain *c; 1582c8befdd5SWarner Losh struct mbuf *m_head; 1583c8befdd5SWarner Losh { 1584c8befdd5SWarner Losh int frag = 0; 1585c8befdd5SWarner Losh struct ste_frag *f = NULL; 1586c8befdd5SWarner Losh struct mbuf *m; 1587c8befdd5SWarner Losh struct ste_desc *d; 1588c8befdd5SWarner Losh 1589c8befdd5SWarner Losh d = c->ste_ptr; 1590c8befdd5SWarner Losh d->ste_ctl = 0; 1591c8befdd5SWarner Losh 1592c8befdd5SWarner Losh encap_retry: 1593c8befdd5SWarner Losh for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 1594c8befdd5SWarner Losh if (m->m_len != 0) { 1595c8befdd5SWarner Losh if (frag == STE_MAXFRAGS) 1596c8befdd5SWarner Losh break; 1597c8befdd5SWarner Losh f = &d->ste_frags[frag]; 1598c8befdd5SWarner Losh f->ste_addr = vtophys(mtod(m, vm_offset_t)); 1599c8befdd5SWarner Losh f->ste_len = m->m_len; 1600c8befdd5SWarner Losh frag++; 1601c8befdd5SWarner Losh } 1602c8befdd5SWarner Losh } 1603c8befdd5SWarner Losh 1604c8befdd5SWarner Losh if (m != NULL) { 1605c8befdd5SWarner Losh struct mbuf *mn; 1606c8befdd5SWarner Losh 1607c8befdd5SWarner Losh /* 1608c8befdd5SWarner Losh * We ran out of segments. We have to recopy this 1609c8befdd5SWarner Losh * mbuf chain first. Bail out if we can't get the 1610c8befdd5SWarner Losh * new buffers. 1611c8befdd5SWarner Losh */ 1612c8befdd5SWarner Losh mn = m_defrag(m_head, M_DONTWAIT); 1613c8befdd5SWarner Losh if (mn == NULL) { 1614c8befdd5SWarner Losh m_freem(m_head); 1615c8befdd5SWarner Losh return ENOMEM; 1616c8befdd5SWarner Losh } 1617c8befdd5SWarner Losh m_head = mn; 1618c8befdd5SWarner Losh goto encap_retry; 1619c8befdd5SWarner Losh } 1620c8befdd5SWarner Losh 1621c8befdd5SWarner Losh c->ste_mbuf = m_head; 1622c8befdd5SWarner Losh d->ste_frags[frag - 1].ste_len |= STE_FRAG_LAST; 1623c8befdd5SWarner Losh d->ste_ctl = 1; 1624c8befdd5SWarner Losh 1625c8befdd5SWarner Losh return(0); 1626c8befdd5SWarner Losh } 1627c8befdd5SWarner Losh 1628c8befdd5SWarner Losh static void 1629c8befdd5SWarner Losh ste_start(ifp) 1630c8befdd5SWarner Losh struct ifnet *ifp; 1631c8befdd5SWarner Losh { 1632c8befdd5SWarner Losh struct ste_softc *sc; 1633c8befdd5SWarner Losh 1634c8befdd5SWarner Losh sc = ifp->if_softc; 1635c8befdd5SWarner Losh STE_LOCK(sc); 1636c8befdd5SWarner Losh ste_start_locked(ifp); 1637c8befdd5SWarner Losh STE_UNLOCK(sc); 1638c8befdd5SWarner Losh } 1639c8befdd5SWarner Losh 1640c8befdd5SWarner Losh static void 1641c8befdd5SWarner Losh ste_start_locked(ifp) 1642c8befdd5SWarner Losh struct ifnet *ifp; 1643c8befdd5SWarner Losh { 1644c8befdd5SWarner Losh struct ste_softc *sc; 1645c8befdd5SWarner Losh struct mbuf *m_head = NULL; 1646c8befdd5SWarner Losh struct ste_chain *cur_tx; 1647c8befdd5SWarner Losh int idx; 1648c8befdd5SWarner Losh 1649c8befdd5SWarner Losh sc = ifp->if_softc; 1650c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1651c8befdd5SWarner Losh 1652c8befdd5SWarner Losh if (!sc->ste_link) 1653c8befdd5SWarner Losh return; 1654c8befdd5SWarner Losh 1655c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 1656c8befdd5SWarner Losh return; 1657c8befdd5SWarner Losh 1658c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_prod; 1659c8befdd5SWarner Losh 1660c8befdd5SWarner Losh while(sc->ste_cdata.ste_tx_chain[idx].ste_mbuf == NULL) { 1661c8befdd5SWarner Losh /* 1662c8befdd5SWarner Losh * We cannot re-use the last (free) descriptor; 1663c8befdd5SWarner Losh * the chip may not have read its ste_next yet. 1664c8befdd5SWarner Losh */ 1665c8befdd5SWarner Losh if (STE_NEXT(idx, STE_TX_LIST_CNT) == 1666c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons) { 1667c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1668c8befdd5SWarner Losh break; 1669c8befdd5SWarner Losh } 1670c8befdd5SWarner Losh 1671c8befdd5SWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 1672c8befdd5SWarner Losh if (m_head == NULL) 1673c8befdd5SWarner Losh break; 1674c8befdd5SWarner Losh 1675c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; 1676c8befdd5SWarner Losh 1677c8befdd5SWarner Losh if (ste_encap(sc, cur_tx, m_head) != 0) 1678c8befdd5SWarner Losh break; 1679c8befdd5SWarner Losh 1680c8befdd5SWarner Losh cur_tx->ste_ptr->ste_next = 0; 1681c8befdd5SWarner Losh 1682c8befdd5SWarner Losh if (sc->ste_tx_prev == NULL) { 1683c8befdd5SWarner Losh cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; 1684c8befdd5SWarner Losh /* Load address of the TX list */ 1685c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1686c8befdd5SWarner Losh ste_wait(sc); 1687c8befdd5SWarner Losh 1688c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 1689c8befdd5SWarner Losh vtophys(&sc->ste_ldata->ste_tx_list[0])); 1690c8befdd5SWarner Losh 1691c8befdd5SWarner Losh /* Set TX polling interval to start TX engine */ 1692c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); 1693c8befdd5SWarner Losh 1694c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1695c8befdd5SWarner Losh ste_wait(sc); 1696c8befdd5SWarner Losh }else{ 1697c8befdd5SWarner Losh cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; 1698c8befdd5SWarner Losh sc->ste_tx_prev->ste_ptr->ste_next 1699c8befdd5SWarner Losh = cur_tx->ste_phys; 1700c8befdd5SWarner Losh } 1701c8befdd5SWarner Losh 1702c8befdd5SWarner Losh sc->ste_tx_prev = cur_tx; 1703c8befdd5SWarner Losh 1704c8befdd5SWarner Losh /* 1705c8befdd5SWarner Losh * If there's a BPF listener, bounce a copy of this frame 1706c8befdd5SWarner Losh * to him. 1707c8befdd5SWarner Losh */ 1708c8befdd5SWarner Losh BPF_MTAP(ifp, cur_tx->ste_mbuf); 1709c8befdd5SWarner Losh 1710c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT); 1711c8befdd5SWarner Losh ifp->if_timer = 5; 1712c8befdd5SWarner Losh } 1713c8befdd5SWarner Losh sc->ste_cdata.ste_tx_prod = idx; 1714c8befdd5SWarner Losh 1715c8befdd5SWarner Losh return; 1716c8befdd5SWarner Losh } 1717c8befdd5SWarner Losh 1718c8befdd5SWarner Losh static void 1719c8befdd5SWarner Losh ste_watchdog(ifp) 1720c8befdd5SWarner Losh struct ifnet *ifp; 1721c8befdd5SWarner Losh { 1722c8befdd5SWarner Losh struct ste_softc *sc; 1723c8befdd5SWarner Losh 1724c8befdd5SWarner Losh sc = ifp->if_softc; 1725c8befdd5SWarner Losh STE_LOCK(sc); 1726c8befdd5SWarner Losh 1727c8befdd5SWarner Losh ifp->if_oerrors++; 1728c8befdd5SWarner Losh if_printf(ifp, "watchdog timeout\n"); 1729c8befdd5SWarner Losh 1730c8befdd5SWarner Losh ste_txeoc(sc); 1731c8befdd5SWarner Losh ste_txeof(sc); 1732c8befdd5SWarner Losh ste_rxeoc(sc); 1733c8befdd5SWarner Losh ste_rxeof(sc); 1734c8befdd5SWarner Losh ste_reset(sc); 1735c8befdd5SWarner Losh ste_init_locked(sc); 1736c8befdd5SWarner Losh 1737c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1738c8befdd5SWarner Losh ste_start_locked(ifp); 1739c8befdd5SWarner Losh STE_UNLOCK(sc); 1740c8befdd5SWarner Losh 1741c8befdd5SWarner Losh return; 1742c8befdd5SWarner Losh } 1743c8befdd5SWarner Losh 1744c8befdd5SWarner Losh static int 1745c8befdd5SWarner Losh ste_shutdown(dev) 1746c8befdd5SWarner Losh device_t dev; 1747c8befdd5SWarner Losh { 1748c8befdd5SWarner Losh struct ste_softc *sc; 1749c8befdd5SWarner Losh 1750c8befdd5SWarner Losh sc = device_get_softc(dev); 1751c8befdd5SWarner Losh 1752c8befdd5SWarner Losh STE_LOCK(sc); 1753c8befdd5SWarner Losh ste_stop(sc); 1754c8befdd5SWarner Losh STE_UNLOCK(sc); 1755c8befdd5SWarner Losh 1756c8befdd5SWarner Losh return (0); 1757c8befdd5SWarner Losh } 1758