1d2155f2fSWarner Losh /*- 2d2155f2fSWarner Losh * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> 3d2155f2fSWarner Losh * Copyright (c) 1997, 1998, 1999 4d2155f2fSWarner Losh * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 5d2155f2fSWarner Losh * 6d2155f2fSWarner Losh * Redistribution and use in source and binary forms, with or without 7d2155f2fSWarner Losh * modification, are permitted provided that the following conditions 8d2155f2fSWarner Losh * are met: 9d2155f2fSWarner Losh * 1. Redistributions of source code must retain the above copyright 10d2155f2fSWarner Losh * notice, this list of conditions and the following disclaimer. 11d2155f2fSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 12d2155f2fSWarner Losh * notice, this list of conditions and the following disclaimer in the 13d2155f2fSWarner Losh * documentation and/or other materials provided with the distribution. 14d2155f2fSWarner Losh * 3. All advertising materials mentioning features or use of this software 15d2155f2fSWarner Losh * must display the following acknowledgement: 16d2155f2fSWarner Losh * This product includes software developed by Bill Paul. 17d2155f2fSWarner Losh * 4. Neither the name of the author nor the names of any co-contributors 18d2155f2fSWarner Losh * may be used to endorse or promote products derived from this software 19d2155f2fSWarner Losh * without specific prior written permission. 20d2155f2fSWarner Losh * 21d2155f2fSWarner Losh * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 22d2155f2fSWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23d2155f2fSWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24d2155f2fSWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 25d2155f2fSWarner Losh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26d2155f2fSWarner Losh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27d2155f2fSWarner Losh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28d2155f2fSWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29d2155f2fSWarner Losh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30d2155f2fSWarner Losh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31d2155f2fSWarner Losh * THE POSSIBILITY OF SUCH DAMAGE. 32d2155f2fSWarner Losh */ 33d2155f2fSWarner Losh 34d2155f2fSWarner Losh #include <sys/cdefs.h> 35d2155f2fSWarner Losh __FBSDID("$FreeBSD$"); 36d2155f2fSWarner Losh 37d2155f2fSWarner Losh /* 38d2155f2fSWarner Losh * SiS 900/SiS 7016 fast ethernet PCI NIC driver. Datasheets are 39d2155f2fSWarner Losh * available from http://www.sis.com.tw. 40d2155f2fSWarner Losh * 41d2155f2fSWarner Losh * This driver also supports the NatSemi DP83815. Datasheets are 42d2155f2fSWarner Losh * available from http://www.national.com. 43d2155f2fSWarner Losh * 44d2155f2fSWarner Losh * Written by Bill Paul <wpaul@ee.columbia.edu> 45d2155f2fSWarner Losh * Electrical Engineering Department 46d2155f2fSWarner Losh * Columbia University, New York City 47d2155f2fSWarner Losh */ 48d2155f2fSWarner Losh /* 49d2155f2fSWarner Losh * The SiS 900 is a fairly simple chip. It uses bus master DMA with 50d2155f2fSWarner Losh * simple TX and RX descriptors of 3 longwords in size. The receiver 51d2155f2fSWarner Losh * has a single perfect filter entry for the station address and a 52d2155f2fSWarner Losh * 128-bit multicast hash table. The SiS 900 has a built-in MII-based 53d2155f2fSWarner Losh * transceiver while the 7016 requires an external transceiver chip. 54d2155f2fSWarner Losh * Both chips offer the standard bit-bang MII interface as well as 55d2155f2fSWarner Losh * an enchanced PHY interface which simplifies accessing MII registers. 56d2155f2fSWarner Losh * 57d2155f2fSWarner Losh * The only downside to this chipset is that RX descriptors must be 58d2155f2fSWarner Losh * longword aligned. 59d2155f2fSWarner Losh */ 60d2155f2fSWarner Losh 61d2155f2fSWarner Losh #ifdef HAVE_KERNEL_OPTION_HEADERS 62d2155f2fSWarner Losh #include "opt_device_polling.h" 63d2155f2fSWarner Losh #endif 64d2155f2fSWarner Losh 65d2155f2fSWarner Losh #include <sys/param.h> 66d2155f2fSWarner Losh #include <sys/systm.h> 67d2155f2fSWarner Losh #include <sys/sockio.h> 68d2155f2fSWarner Losh #include <sys/mbuf.h> 69d2155f2fSWarner Losh #include <sys/malloc.h> 70d2155f2fSWarner Losh #include <sys/kernel.h> 71d2155f2fSWarner Losh #include <sys/module.h> 72d2155f2fSWarner Losh #include <sys/socket.h> 73d2155f2fSWarner Losh 74d2155f2fSWarner Losh #include <net/if.h> 75d2155f2fSWarner Losh #include <net/if_arp.h> 76d2155f2fSWarner Losh #include <net/ethernet.h> 77d2155f2fSWarner Losh #include <net/if_dl.h> 78d2155f2fSWarner Losh #include <net/if_media.h> 79d2155f2fSWarner Losh #include <net/if_types.h> 80d2155f2fSWarner Losh #include <net/if_vlan_var.h> 81d2155f2fSWarner Losh 82d2155f2fSWarner Losh #include <net/bpf.h> 83d2155f2fSWarner Losh 84d2155f2fSWarner Losh #include <machine/bus.h> 85d2155f2fSWarner Losh #include <machine/resource.h> 86d2155f2fSWarner Losh #include <sys/bus.h> 87d2155f2fSWarner Losh #include <sys/rman.h> 88d2155f2fSWarner Losh 89d2155f2fSWarner Losh #include <dev/mii/mii.h> 90d2155f2fSWarner Losh #include <dev/mii/miivar.h> 91d2155f2fSWarner Losh 92d2155f2fSWarner Losh #include <dev/pci/pcireg.h> 93d2155f2fSWarner Losh #include <dev/pci/pcivar.h> 94d2155f2fSWarner Losh 95d2155f2fSWarner Losh #define SIS_USEIOSPACE 96d2155f2fSWarner Losh 97d2155f2fSWarner Losh #include <dev/sis/if_sisreg.h> 98d2155f2fSWarner Losh 99d2155f2fSWarner Losh MODULE_DEPEND(sis, pci, 1, 1, 1); 100d2155f2fSWarner Losh MODULE_DEPEND(sis, ether, 1, 1, 1); 101d2155f2fSWarner Losh MODULE_DEPEND(sis, miibus, 1, 1, 1); 102d2155f2fSWarner Losh 103d2155f2fSWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 104d2155f2fSWarner Losh #include "miibus_if.h" 105d2155f2fSWarner Losh 106d2155f2fSWarner Losh #define SIS_LOCK(_sc) mtx_lock(&(_sc)->sis_mtx) 107d2155f2fSWarner Losh #define SIS_UNLOCK(_sc) mtx_unlock(&(_sc)->sis_mtx) 108d2155f2fSWarner Losh #define SIS_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sis_mtx, MA_OWNED) 109d2155f2fSWarner Losh 110d2155f2fSWarner Losh /* 111d2155f2fSWarner Losh * register space access macros 112d2155f2fSWarner Losh */ 113d2155f2fSWarner Losh #define CSR_WRITE_4(sc, reg, val) bus_write_4(sc->sis_res[0], reg, val) 114d2155f2fSWarner Losh 115d2155f2fSWarner Losh #define CSR_READ_4(sc, reg) bus_read_4(sc->sis_res[0], reg) 116d2155f2fSWarner Losh 117d2155f2fSWarner Losh #define CSR_READ_2(sc, reg) bus_read_2(sc->sis_res[0], reg) 118d2155f2fSWarner Losh 119d2155f2fSWarner Losh /* 120d2155f2fSWarner Losh * Various supported device vendors/types and their names. 121d2155f2fSWarner Losh */ 122d2155f2fSWarner Losh static struct sis_type sis_devs[] = { 123d2155f2fSWarner Losh { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, 124d2155f2fSWarner Losh { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, 125d2155f2fSWarner Losh { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP8381[56] 10/100BaseTX" }, 126d2155f2fSWarner Losh { 0, 0, NULL } 127d2155f2fSWarner Losh }; 128d2155f2fSWarner Losh 129d2155f2fSWarner Losh static int sis_detach(device_t); 130d2155f2fSWarner Losh static void sis_ifmedia_sts(struct ifnet *, struct ifmediareq *); 131d2155f2fSWarner Losh static int sis_ifmedia_upd(struct ifnet *); 132d2155f2fSWarner Losh static void sis_init(void *); 133d2155f2fSWarner Losh static void sis_initl(struct sis_softc *); 134d2155f2fSWarner Losh static void sis_intr(void *); 135d2155f2fSWarner Losh static int sis_ioctl(struct ifnet *, u_long, caddr_t); 136d2155f2fSWarner Losh static int sis_newbuf(struct sis_softc *, struct sis_desc *, struct mbuf *); 137d2155f2fSWarner Losh static void sis_start(struct ifnet *); 138d2155f2fSWarner Losh static void sis_startl(struct ifnet *); 139d2155f2fSWarner Losh static void sis_stop(struct sis_softc *); 140d2155f2fSWarner Losh static void sis_watchdog(struct sis_softc *); 141d2155f2fSWarner Losh 142d2155f2fSWarner Losh 143d2155f2fSWarner Losh static struct resource_spec sis_res_spec[] = { 144d2155f2fSWarner Losh #ifdef SIS_USEIOSPACE 145d2155f2fSWarner Losh { SYS_RES_IOPORT, SIS_PCI_LOIO, RF_ACTIVE}, 146d2155f2fSWarner Losh #else 147d2155f2fSWarner Losh { SYS_RES_MEMORY, SIS_PCI_LOMEM, RF_ACTIVE}, 148d2155f2fSWarner Losh #endif 149d2155f2fSWarner Losh { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 150d2155f2fSWarner Losh { -1, 0 } 151d2155f2fSWarner Losh }; 152d2155f2fSWarner Losh 153d2155f2fSWarner Losh #define SIS_SETBIT(sc, reg, x) \ 154d2155f2fSWarner Losh CSR_WRITE_4(sc, reg, \ 155d2155f2fSWarner Losh CSR_READ_4(sc, reg) | (x)) 156d2155f2fSWarner Losh 157d2155f2fSWarner Losh #define SIS_CLRBIT(sc, reg, x) \ 158d2155f2fSWarner Losh CSR_WRITE_4(sc, reg, \ 159d2155f2fSWarner Losh CSR_READ_4(sc, reg) & ~(x)) 160d2155f2fSWarner Losh 161d2155f2fSWarner Losh #define SIO_SET(x) \ 162d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) 163d2155f2fSWarner Losh 164d2155f2fSWarner Losh #define SIO_CLR(x) \ 165d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) 166d2155f2fSWarner Losh 167d2155f2fSWarner Losh static void 168d2155f2fSWarner Losh sis_dma_map_desc_next(void *arg, bus_dma_segment_t *segs, int nseg, int error) 169d2155f2fSWarner Losh { 170d2155f2fSWarner Losh struct sis_desc *r; 171d2155f2fSWarner Losh 172d2155f2fSWarner Losh r = arg; 173d2155f2fSWarner Losh r->sis_next = segs->ds_addr; 174d2155f2fSWarner Losh } 175d2155f2fSWarner Losh 176d2155f2fSWarner Losh static void 177d2155f2fSWarner Losh sis_dma_map_desc_ptr(void *arg, bus_dma_segment_t *segs, int nseg, int error) 178d2155f2fSWarner Losh { 179d2155f2fSWarner Losh struct sis_desc *r; 180d2155f2fSWarner Losh 181d2155f2fSWarner Losh r = arg; 182d2155f2fSWarner Losh r->sis_ptr = segs->ds_addr; 183d2155f2fSWarner Losh } 184d2155f2fSWarner Losh 185d2155f2fSWarner Losh static void 186d2155f2fSWarner Losh sis_dma_map_ring(void *arg, bus_dma_segment_t *segs, int nseg, int error) 187d2155f2fSWarner Losh { 188d2155f2fSWarner Losh u_int32_t *p; 189d2155f2fSWarner Losh 190d2155f2fSWarner Losh p = arg; 191d2155f2fSWarner Losh *p = segs->ds_addr; 192d2155f2fSWarner Losh } 193d2155f2fSWarner Losh 194d2155f2fSWarner Losh /* 195d2155f2fSWarner Losh * Routine to reverse the bits in a word. Stolen almost 196d2155f2fSWarner Losh * verbatim from /usr/games/fortune. 197d2155f2fSWarner Losh */ 198d2155f2fSWarner Losh static uint16_t 199d2155f2fSWarner Losh sis_reverse(uint16_t n) 200d2155f2fSWarner Losh { 201d2155f2fSWarner Losh n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); 202d2155f2fSWarner Losh n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); 203d2155f2fSWarner Losh n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); 204d2155f2fSWarner Losh n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); 205d2155f2fSWarner Losh 206d2155f2fSWarner Losh return(n); 207d2155f2fSWarner Losh } 208d2155f2fSWarner Losh 209d2155f2fSWarner Losh static void 210d2155f2fSWarner Losh sis_delay(struct sis_softc *sc) 211d2155f2fSWarner Losh { 212d2155f2fSWarner Losh int idx; 213d2155f2fSWarner Losh 214d2155f2fSWarner Losh for (idx = (300 / 33) + 1; idx > 0; idx--) 215d2155f2fSWarner Losh CSR_READ_4(sc, SIS_CSR); 216d2155f2fSWarner Losh } 217d2155f2fSWarner Losh 218d2155f2fSWarner Losh static void 219d2155f2fSWarner Losh sis_eeprom_idle(struct sis_softc *sc) 220d2155f2fSWarner Losh { 221d2155f2fSWarner Losh int i; 222d2155f2fSWarner Losh 223d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CSEL); 224d2155f2fSWarner Losh sis_delay(sc); 225d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 226d2155f2fSWarner Losh sis_delay(sc); 227d2155f2fSWarner Losh 228d2155f2fSWarner Losh for (i = 0; i < 25; i++) { 229d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 230d2155f2fSWarner Losh sis_delay(sc); 231d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 232d2155f2fSWarner Losh sis_delay(sc); 233d2155f2fSWarner Losh } 234d2155f2fSWarner Losh 235d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 236d2155f2fSWarner Losh sis_delay(sc); 237d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CSEL); 238d2155f2fSWarner Losh sis_delay(sc); 239d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); 240d2155f2fSWarner Losh } 241d2155f2fSWarner Losh 242d2155f2fSWarner Losh /* 243d2155f2fSWarner Losh * Send a read command and address to the EEPROM, check for ACK. 244d2155f2fSWarner Losh */ 245d2155f2fSWarner Losh static void 246d2155f2fSWarner Losh sis_eeprom_putbyte(struct sis_softc *sc, int addr) 247d2155f2fSWarner Losh { 248d2155f2fSWarner Losh int d, i; 249d2155f2fSWarner Losh 250d2155f2fSWarner Losh d = addr | SIS_EECMD_READ; 251d2155f2fSWarner Losh 252d2155f2fSWarner Losh /* 253d2155f2fSWarner Losh * Feed in each bit and stobe the clock. 254d2155f2fSWarner Losh */ 255d2155f2fSWarner Losh for (i = 0x400; i; i >>= 1) { 256d2155f2fSWarner Losh if (d & i) { 257d2155f2fSWarner Losh SIO_SET(SIS_EECTL_DIN); 258d2155f2fSWarner Losh } else { 259d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_DIN); 260d2155f2fSWarner Losh } 261d2155f2fSWarner Losh sis_delay(sc); 262d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 263d2155f2fSWarner Losh sis_delay(sc); 264d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 265d2155f2fSWarner Losh sis_delay(sc); 266d2155f2fSWarner Losh } 267d2155f2fSWarner Losh } 268d2155f2fSWarner Losh 269d2155f2fSWarner Losh /* 270d2155f2fSWarner Losh * Read a word of data stored in the EEPROM at address 'addr.' 271d2155f2fSWarner Losh */ 272d2155f2fSWarner Losh static void 273d2155f2fSWarner Losh sis_eeprom_getword(struct sis_softc *sc, int addr, uint16_t *dest) 274d2155f2fSWarner Losh { 275d2155f2fSWarner Losh int i; 276d2155f2fSWarner Losh u_int16_t word = 0; 277d2155f2fSWarner Losh 278d2155f2fSWarner Losh /* Force EEPROM to idle state. */ 279d2155f2fSWarner Losh sis_eeprom_idle(sc); 280d2155f2fSWarner Losh 281d2155f2fSWarner Losh /* Enter EEPROM access mode. */ 282d2155f2fSWarner Losh sis_delay(sc); 283d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 284d2155f2fSWarner Losh sis_delay(sc); 285d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CSEL); 286d2155f2fSWarner Losh sis_delay(sc); 287d2155f2fSWarner Losh 288d2155f2fSWarner Losh /* 289d2155f2fSWarner Losh * Send address of word we want to read. 290d2155f2fSWarner Losh */ 291d2155f2fSWarner Losh sis_eeprom_putbyte(sc, addr); 292d2155f2fSWarner Losh 293d2155f2fSWarner Losh /* 294d2155f2fSWarner Losh * Start reading bits from EEPROM. 295d2155f2fSWarner Losh */ 296d2155f2fSWarner Losh for (i = 0x8000; i; i >>= 1) { 297d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 298d2155f2fSWarner Losh sis_delay(sc); 299d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) 300d2155f2fSWarner Losh word |= i; 301d2155f2fSWarner Losh sis_delay(sc); 302d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 303d2155f2fSWarner Losh sis_delay(sc); 304d2155f2fSWarner Losh } 305d2155f2fSWarner Losh 306d2155f2fSWarner Losh /* Turn off EEPROM access mode. */ 307d2155f2fSWarner Losh sis_eeprom_idle(sc); 308d2155f2fSWarner Losh 309d2155f2fSWarner Losh *dest = word; 310d2155f2fSWarner Losh } 311d2155f2fSWarner Losh 312d2155f2fSWarner Losh /* 313d2155f2fSWarner Losh * Read a sequence of words from the EEPROM. 314d2155f2fSWarner Losh */ 315d2155f2fSWarner Losh static void 316d2155f2fSWarner Losh sis_read_eeprom(struct sis_softc *sc, caddr_t dest, int off, int cnt, int swap) 317d2155f2fSWarner Losh { 318d2155f2fSWarner Losh int i; 319d2155f2fSWarner Losh u_int16_t word = 0, *ptr; 320d2155f2fSWarner Losh 321d2155f2fSWarner Losh for (i = 0; i < cnt; i++) { 322d2155f2fSWarner Losh sis_eeprom_getword(sc, off + i, &word); 323d2155f2fSWarner Losh ptr = (u_int16_t *)(dest + (i * 2)); 324d2155f2fSWarner Losh if (swap) 325d2155f2fSWarner Losh *ptr = ntohs(word); 326d2155f2fSWarner Losh else 327d2155f2fSWarner Losh *ptr = word; 328d2155f2fSWarner Losh } 329d2155f2fSWarner Losh } 330d2155f2fSWarner Losh 331d2155f2fSWarner Losh #if defined(__i386__) || defined(__amd64__) 332d2155f2fSWarner Losh static device_t 333d2155f2fSWarner Losh sis_find_bridge(device_t dev) 334d2155f2fSWarner Losh { 335d2155f2fSWarner Losh devclass_t pci_devclass; 336d2155f2fSWarner Losh device_t *pci_devices; 337d2155f2fSWarner Losh int pci_count = 0; 338d2155f2fSWarner Losh device_t *pci_children; 339d2155f2fSWarner Losh int pci_childcount = 0; 340d2155f2fSWarner Losh device_t *busp, *childp; 341d2155f2fSWarner Losh device_t child = NULL; 342d2155f2fSWarner Losh int i, j; 343d2155f2fSWarner Losh 344d2155f2fSWarner Losh if ((pci_devclass = devclass_find("pci")) == NULL) 345d2155f2fSWarner Losh return(NULL); 346d2155f2fSWarner Losh 347d2155f2fSWarner Losh devclass_get_devices(pci_devclass, &pci_devices, &pci_count); 348d2155f2fSWarner Losh 349d2155f2fSWarner Losh for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) { 350d2155f2fSWarner Losh pci_childcount = 0; 351d2155f2fSWarner Losh device_get_children(*busp, &pci_children, &pci_childcount); 352d2155f2fSWarner Losh for (j = 0, childp = pci_children; 353d2155f2fSWarner Losh j < pci_childcount; j++, childp++) { 354d2155f2fSWarner Losh if (pci_get_vendor(*childp) == SIS_VENDORID && 355d2155f2fSWarner Losh pci_get_device(*childp) == 0x0008) { 356d2155f2fSWarner Losh child = *childp; 357d2155f2fSWarner Losh goto done; 358d2155f2fSWarner Losh } 359d2155f2fSWarner Losh } 360d2155f2fSWarner Losh } 361d2155f2fSWarner Losh 362d2155f2fSWarner Losh done: 363d2155f2fSWarner Losh free(pci_devices, M_TEMP); 364d2155f2fSWarner Losh free(pci_children, M_TEMP); 365d2155f2fSWarner Losh return(child); 366d2155f2fSWarner Losh } 367d2155f2fSWarner Losh 368d2155f2fSWarner Losh static void 369d2155f2fSWarner Losh sis_read_cmos(struct sis_softc *sc, device_t dev, caddr_t dest, int off, int cnt) 370d2155f2fSWarner Losh { 371d2155f2fSWarner Losh device_t bridge; 372d2155f2fSWarner Losh u_int8_t reg; 373d2155f2fSWarner Losh int i; 374d2155f2fSWarner Losh bus_space_tag_t btag; 375d2155f2fSWarner Losh 376d2155f2fSWarner Losh bridge = sis_find_bridge(dev); 377d2155f2fSWarner Losh if (bridge == NULL) 378d2155f2fSWarner Losh return; 379d2155f2fSWarner Losh reg = pci_read_config(bridge, 0x48, 1); 380d2155f2fSWarner Losh pci_write_config(bridge, 0x48, reg|0x40, 1); 381d2155f2fSWarner Losh 382d2155f2fSWarner Losh /* XXX */ 383d2155f2fSWarner Losh #if defined(__i386__) 384d2155f2fSWarner Losh btag = I386_BUS_SPACE_IO; 385d2155f2fSWarner Losh #elif defined(__amd64__) 386d2155f2fSWarner Losh btag = AMD64_BUS_SPACE_IO; 387d2155f2fSWarner Losh #endif 388d2155f2fSWarner Losh 389d2155f2fSWarner Losh for (i = 0; i < cnt; i++) { 390d2155f2fSWarner Losh bus_space_write_1(btag, 0x0, 0x70, i + off); 391d2155f2fSWarner Losh *(dest + i) = bus_space_read_1(btag, 0x0, 0x71); 392d2155f2fSWarner Losh } 393d2155f2fSWarner Losh 394d2155f2fSWarner Losh pci_write_config(bridge, 0x48, reg & ~0x40, 1); 395d2155f2fSWarner Losh return; 396d2155f2fSWarner Losh } 397d2155f2fSWarner Losh 398d2155f2fSWarner Losh static void 399d2155f2fSWarner Losh sis_read_mac(struct sis_softc *sc, device_t dev, caddr_t dest) 400d2155f2fSWarner Losh { 401d2155f2fSWarner Losh u_int32_t filtsave, csrsave; 402d2155f2fSWarner Losh 403d2155f2fSWarner Losh filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 404d2155f2fSWarner Losh csrsave = CSR_READ_4(sc, SIS_CSR); 405d2155f2fSWarner Losh 406d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, SIS_CSR_RELOAD | filtsave); 407d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, 0); 408d2155f2fSWarner Losh 409d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave & ~SIS_RXFILTCTL_ENABLE); 410d2155f2fSWarner Losh 411d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 412d2155f2fSWarner Losh ((u_int16_t *)dest)[0] = CSR_READ_2(sc, SIS_RXFILT_DATA); 413d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL,SIS_FILTADDR_PAR1); 414d2155f2fSWarner Losh ((u_int16_t *)dest)[1] = CSR_READ_2(sc, SIS_RXFILT_DATA); 415d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 416d2155f2fSWarner Losh ((u_int16_t *)dest)[2] = CSR_READ_2(sc, SIS_RXFILT_DATA); 417d2155f2fSWarner Losh 418d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 419d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, csrsave); 420d2155f2fSWarner Losh return; 421d2155f2fSWarner Losh } 422d2155f2fSWarner Losh #endif 423d2155f2fSWarner Losh 424d2155f2fSWarner Losh /* 425d2155f2fSWarner Losh * Sync the PHYs by setting data bit and strobing the clock 32 times. 426d2155f2fSWarner Losh */ 427d2155f2fSWarner Losh static void 428d2155f2fSWarner Losh sis_mii_sync(struct sis_softc *sc) 429d2155f2fSWarner Losh { 430d2155f2fSWarner Losh int i; 431d2155f2fSWarner Losh 432d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR|SIS_MII_DATA); 433d2155f2fSWarner Losh 434d2155f2fSWarner Losh for (i = 0; i < 32; i++) { 435d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 436d2155f2fSWarner Losh DELAY(1); 437d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 438d2155f2fSWarner Losh DELAY(1); 439d2155f2fSWarner Losh } 440d2155f2fSWarner Losh } 441d2155f2fSWarner Losh 442d2155f2fSWarner Losh /* 443d2155f2fSWarner Losh * Clock a series of bits through the MII. 444d2155f2fSWarner Losh */ 445d2155f2fSWarner Losh static void 446d2155f2fSWarner Losh sis_mii_send(struct sis_softc *sc, uint32_t bits, int cnt) 447d2155f2fSWarner Losh { 448d2155f2fSWarner Losh int i; 449d2155f2fSWarner Losh 450d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 451d2155f2fSWarner Losh 452d2155f2fSWarner Losh for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 453d2155f2fSWarner Losh if (bits & i) { 454d2155f2fSWarner Losh SIO_SET(SIS_MII_DATA); 455d2155f2fSWarner Losh } else { 456d2155f2fSWarner Losh SIO_CLR(SIS_MII_DATA); 457d2155f2fSWarner Losh } 458d2155f2fSWarner Losh DELAY(1); 459d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 460d2155f2fSWarner Losh DELAY(1); 461d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 462d2155f2fSWarner Losh } 463d2155f2fSWarner Losh } 464d2155f2fSWarner Losh 465d2155f2fSWarner Losh /* 466d2155f2fSWarner Losh * Read an PHY register through the MII. 467d2155f2fSWarner Losh */ 468d2155f2fSWarner Losh static int 469d2155f2fSWarner Losh sis_mii_readreg(struct sis_softc *sc, struct sis_mii_frame *frame) 470d2155f2fSWarner Losh { 471d2155f2fSWarner Losh int i, ack; 472d2155f2fSWarner Losh 473d2155f2fSWarner Losh /* 474d2155f2fSWarner Losh * Set up frame for RX. 475d2155f2fSWarner Losh */ 476d2155f2fSWarner Losh frame->mii_stdelim = SIS_MII_STARTDELIM; 477d2155f2fSWarner Losh frame->mii_opcode = SIS_MII_READOP; 478d2155f2fSWarner Losh frame->mii_turnaround = 0; 479d2155f2fSWarner Losh frame->mii_data = 0; 480d2155f2fSWarner Losh 481d2155f2fSWarner Losh /* 482d2155f2fSWarner Losh * Turn on data xmit. 483d2155f2fSWarner Losh */ 484d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR); 485d2155f2fSWarner Losh 486d2155f2fSWarner Losh sis_mii_sync(sc); 487d2155f2fSWarner Losh 488d2155f2fSWarner Losh /* 489d2155f2fSWarner Losh * Send command/address info. 490d2155f2fSWarner Losh */ 491d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_stdelim, 2); 492d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_opcode, 2); 493d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_phyaddr, 5); 494d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_regaddr, 5); 495d2155f2fSWarner Losh 496d2155f2fSWarner Losh /* Idle bit */ 497d2155f2fSWarner Losh SIO_CLR((SIS_MII_CLK|SIS_MII_DATA)); 498d2155f2fSWarner Losh DELAY(1); 499d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 500d2155f2fSWarner Losh DELAY(1); 501d2155f2fSWarner Losh 502d2155f2fSWarner Losh /* Turn off xmit. */ 503d2155f2fSWarner Losh SIO_CLR(SIS_MII_DIR); 504d2155f2fSWarner Losh 505d2155f2fSWarner Losh /* Check for ack */ 506d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 507d2155f2fSWarner Losh DELAY(1); 508d2155f2fSWarner Losh ack = CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA; 509d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 510d2155f2fSWarner Losh DELAY(1); 511d2155f2fSWarner Losh 512d2155f2fSWarner Losh /* 513d2155f2fSWarner Losh * Now try reading data bits. If the ack failed, we still 514d2155f2fSWarner Losh * need to clock through 16 cycles to keep the PHY(s) in sync. 515d2155f2fSWarner Losh */ 516d2155f2fSWarner Losh if (ack) { 517d2155f2fSWarner Losh for(i = 0; i < 16; i++) { 518d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 519d2155f2fSWarner Losh DELAY(1); 520d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 521d2155f2fSWarner Losh DELAY(1); 522d2155f2fSWarner Losh } 523d2155f2fSWarner Losh goto fail; 524d2155f2fSWarner Losh } 525d2155f2fSWarner Losh 526d2155f2fSWarner Losh for (i = 0x8000; i; i >>= 1) { 527d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 528d2155f2fSWarner Losh DELAY(1); 529d2155f2fSWarner Losh if (!ack) { 530d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA) 531d2155f2fSWarner Losh frame->mii_data |= i; 532d2155f2fSWarner Losh DELAY(1); 533d2155f2fSWarner Losh } 534d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 535d2155f2fSWarner Losh DELAY(1); 536d2155f2fSWarner Losh } 537d2155f2fSWarner Losh 538d2155f2fSWarner Losh fail: 539d2155f2fSWarner Losh 540d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 541d2155f2fSWarner Losh DELAY(1); 542d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 543d2155f2fSWarner Losh DELAY(1); 544d2155f2fSWarner Losh 545d2155f2fSWarner Losh if (ack) 546d2155f2fSWarner Losh return(1); 547d2155f2fSWarner Losh return(0); 548d2155f2fSWarner Losh } 549d2155f2fSWarner Losh 550d2155f2fSWarner Losh /* 551d2155f2fSWarner Losh * Write to a PHY register through the MII. 552d2155f2fSWarner Losh */ 553d2155f2fSWarner Losh static int 554d2155f2fSWarner Losh sis_mii_writereg(struct sis_softc *sc, struct sis_mii_frame *frame) 555d2155f2fSWarner Losh { 556d2155f2fSWarner Losh 557d2155f2fSWarner Losh /* 558d2155f2fSWarner Losh * Set up frame for TX. 559d2155f2fSWarner Losh */ 560d2155f2fSWarner Losh 561d2155f2fSWarner Losh frame->mii_stdelim = SIS_MII_STARTDELIM; 562d2155f2fSWarner Losh frame->mii_opcode = SIS_MII_WRITEOP; 563d2155f2fSWarner Losh frame->mii_turnaround = SIS_MII_TURNAROUND; 564d2155f2fSWarner Losh 565d2155f2fSWarner Losh /* 566d2155f2fSWarner Losh * Turn on data output. 567d2155f2fSWarner Losh */ 568d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR); 569d2155f2fSWarner Losh 570d2155f2fSWarner Losh sis_mii_sync(sc); 571d2155f2fSWarner Losh 572d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_stdelim, 2); 573d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_opcode, 2); 574d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_phyaddr, 5); 575d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_regaddr, 5); 576d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_turnaround, 2); 577d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_data, 16); 578d2155f2fSWarner Losh 579d2155f2fSWarner Losh /* Idle bit. */ 580d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 581d2155f2fSWarner Losh DELAY(1); 582d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 583d2155f2fSWarner Losh DELAY(1); 584d2155f2fSWarner Losh 585d2155f2fSWarner Losh /* 586d2155f2fSWarner Losh * Turn off xmit. 587d2155f2fSWarner Losh */ 588d2155f2fSWarner Losh SIO_CLR(SIS_MII_DIR); 589d2155f2fSWarner Losh 590d2155f2fSWarner Losh return(0); 591d2155f2fSWarner Losh } 592d2155f2fSWarner Losh 593d2155f2fSWarner Losh static int 594d2155f2fSWarner Losh sis_miibus_readreg(device_t dev, int phy, int reg) 595d2155f2fSWarner Losh { 596d2155f2fSWarner Losh struct sis_softc *sc; 597d2155f2fSWarner Losh struct sis_mii_frame frame; 598d2155f2fSWarner Losh 599d2155f2fSWarner Losh sc = device_get_softc(dev); 600d2155f2fSWarner Losh 601d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 602d2155f2fSWarner Losh if (phy != 0) 603d2155f2fSWarner Losh return(0); 604d2155f2fSWarner Losh /* 605d2155f2fSWarner Losh * The NatSemi chip can take a while after 606d2155f2fSWarner Losh * a reset to come ready, during which the BMSR 607d2155f2fSWarner Losh * returns a value of 0. This is *never* supposed 608d2155f2fSWarner Losh * to happen: some of the BMSR bits are meant to 609d2155f2fSWarner Losh * be hardwired in the on position, and this can 610d2155f2fSWarner Losh * confuse the miibus code a bit during the probe 611d2155f2fSWarner Losh * and attach phase. So we make an effort to check 612d2155f2fSWarner Losh * for this condition and wait for it to clear. 613d2155f2fSWarner Losh */ 614d2155f2fSWarner Losh if (!CSR_READ_4(sc, NS_BMSR)) 615d2155f2fSWarner Losh DELAY(1000); 616d2155f2fSWarner Losh return CSR_READ_4(sc, NS_BMCR + (reg * 4)); 617d2155f2fSWarner Losh } 618d2155f2fSWarner Losh 619d2155f2fSWarner Losh /* 620d2155f2fSWarner Losh * Chipsets < SIS_635 seem not to be able to read/write 621d2155f2fSWarner Losh * through mdio. Use the enhanced PHY access register 622d2155f2fSWarner Losh * again for them. 623d2155f2fSWarner Losh */ 624d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 625d2155f2fSWarner Losh sc->sis_rev < SIS_REV_635) { 626d2155f2fSWarner Losh int i, val = 0; 627d2155f2fSWarner Losh 628d2155f2fSWarner Losh if (phy != 0) 629d2155f2fSWarner Losh return(0); 630d2155f2fSWarner Losh 631d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_PHYCTL, 632d2155f2fSWarner Losh (phy << 11) | (reg << 6) | SIS_PHYOP_READ); 633d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 634d2155f2fSWarner Losh 635d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 636d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 637d2155f2fSWarner Losh break; 638d2155f2fSWarner Losh } 639d2155f2fSWarner Losh 640d2155f2fSWarner Losh if (i == SIS_TIMEOUT) { 641d2155f2fSWarner Losh device_printf(sc->sis_dev, "PHY failed to come ready\n"); 642d2155f2fSWarner Losh return(0); 643d2155f2fSWarner Losh } 644d2155f2fSWarner Losh 645d2155f2fSWarner Losh val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; 646d2155f2fSWarner Losh 647d2155f2fSWarner Losh if (val == 0xFFFF) 648d2155f2fSWarner Losh return(0); 649d2155f2fSWarner Losh 650d2155f2fSWarner Losh return(val); 651d2155f2fSWarner Losh } else { 652d2155f2fSWarner Losh bzero((char *)&frame, sizeof(frame)); 653d2155f2fSWarner Losh 654d2155f2fSWarner Losh frame.mii_phyaddr = phy; 655d2155f2fSWarner Losh frame.mii_regaddr = reg; 656d2155f2fSWarner Losh sis_mii_readreg(sc, &frame); 657d2155f2fSWarner Losh 658d2155f2fSWarner Losh return(frame.mii_data); 659d2155f2fSWarner Losh } 660d2155f2fSWarner Losh } 661d2155f2fSWarner Losh 662d2155f2fSWarner Losh static int 663d2155f2fSWarner Losh sis_miibus_writereg(device_t dev, int phy, int reg, int data) 664d2155f2fSWarner Losh { 665d2155f2fSWarner Losh struct sis_softc *sc; 666d2155f2fSWarner Losh struct sis_mii_frame frame; 667d2155f2fSWarner Losh 668d2155f2fSWarner Losh sc = device_get_softc(dev); 669d2155f2fSWarner Losh 670d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 671d2155f2fSWarner Losh if (phy != 0) 672d2155f2fSWarner Losh return(0); 673d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); 674d2155f2fSWarner Losh return(0); 675d2155f2fSWarner Losh } 676d2155f2fSWarner Losh 677d2155f2fSWarner Losh /* 678d2155f2fSWarner Losh * Chipsets < SIS_635 seem not to be able to read/write 679d2155f2fSWarner Losh * through mdio. Use the enhanced PHY access register 680d2155f2fSWarner Losh * again for them. 681d2155f2fSWarner Losh */ 682d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 683d2155f2fSWarner Losh sc->sis_rev < SIS_REV_635) { 684d2155f2fSWarner Losh int i; 685d2155f2fSWarner Losh 686d2155f2fSWarner Losh if (phy != 0) 687d2155f2fSWarner Losh return(0); 688d2155f2fSWarner Losh 689d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | 690d2155f2fSWarner Losh (reg << 6) | SIS_PHYOP_WRITE); 691d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 692d2155f2fSWarner Losh 693d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 694d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 695d2155f2fSWarner Losh break; 696d2155f2fSWarner Losh } 697d2155f2fSWarner Losh 698d2155f2fSWarner Losh if (i == SIS_TIMEOUT) 699d2155f2fSWarner Losh device_printf(sc->sis_dev, "PHY failed to come ready\n"); 700d2155f2fSWarner Losh } else { 701d2155f2fSWarner Losh bzero((char *)&frame, sizeof(frame)); 702d2155f2fSWarner Losh 703d2155f2fSWarner Losh frame.mii_phyaddr = phy; 704d2155f2fSWarner Losh frame.mii_regaddr = reg; 705d2155f2fSWarner Losh frame.mii_data = data; 706d2155f2fSWarner Losh sis_mii_writereg(sc, &frame); 707d2155f2fSWarner Losh } 708d2155f2fSWarner Losh return(0); 709d2155f2fSWarner Losh } 710d2155f2fSWarner Losh 711d2155f2fSWarner Losh static void 712d2155f2fSWarner Losh sis_miibus_statchg(device_t dev) 713d2155f2fSWarner Losh { 714d2155f2fSWarner Losh struct sis_softc *sc; 715d2155f2fSWarner Losh 716d2155f2fSWarner Losh sc = device_get_softc(dev); 717d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 718d2155f2fSWarner Losh sis_initl(sc); 719d2155f2fSWarner Losh } 720d2155f2fSWarner Losh 721d2155f2fSWarner Losh static uint32_t 722d2155f2fSWarner Losh sis_mchash(struct sis_softc *sc, const uint8_t *addr) 723d2155f2fSWarner Losh { 724d2155f2fSWarner Losh uint32_t crc; 725d2155f2fSWarner Losh 726d2155f2fSWarner Losh /* Compute CRC for the address value. */ 727d2155f2fSWarner Losh crc = ether_crc32_be(addr, ETHER_ADDR_LEN); 728d2155f2fSWarner Losh 729d2155f2fSWarner Losh /* 730d2155f2fSWarner Losh * return the filter bit position 731d2155f2fSWarner Losh * 732d2155f2fSWarner Losh * The NatSemi chip has a 512-bit filter, which is 733d2155f2fSWarner Losh * different than the SiS, so we special-case it. 734d2155f2fSWarner Losh */ 735d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 736d2155f2fSWarner Losh return (crc >> 23); 737d2155f2fSWarner Losh else if (sc->sis_rev >= SIS_REV_635 || 738d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B) 739d2155f2fSWarner Losh return (crc >> 24); 740d2155f2fSWarner Losh else 741d2155f2fSWarner Losh return (crc >> 25); 742d2155f2fSWarner Losh } 743d2155f2fSWarner Losh 744d2155f2fSWarner Losh static void 745d2155f2fSWarner Losh sis_setmulti_ns(struct sis_softc *sc) 746d2155f2fSWarner Losh { 747d2155f2fSWarner Losh struct ifnet *ifp; 748d2155f2fSWarner Losh struct ifmultiaddr *ifma; 749d2155f2fSWarner Losh u_int32_t h = 0, i, filtsave; 750d2155f2fSWarner Losh int bit, index; 751d2155f2fSWarner Losh 752d2155f2fSWarner Losh ifp = sc->sis_ifp; 753d2155f2fSWarner Losh 754d2155f2fSWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 755d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 756d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 757d2155f2fSWarner Losh return; 758d2155f2fSWarner Losh } 759d2155f2fSWarner Losh 760d2155f2fSWarner Losh /* 761d2155f2fSWarner Losh * We have to explicitly enable the multicast hash table 762d2155f2fSWarner Losh * on the NatSemi chip if we want to use it, which we do. 763d2155f2fSWarner Losh */ 764d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 765d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 766d2155f2fSWarner Losh 767d2155f2fSWarner Losh filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 768d2155f2fSWarner Losh 769d2155f2fSWarner Losh /* first, zot all the existing hash bits */ 770d2155f2fSWarner Losh for (i = 0; i < 32; i++) { 771d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); 772d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 773d2155f2fSWarner Losh } 774d2155f2fSWarner Losh 775d2155f2fSWarner Losh IF_ADDR_LOCK(ifp); 776d2155f2fSWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 777d2155f2fSWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 778d2155f2fSWarner Losh continue; 779d2155f2fSWarner Losh h = sis_mchash(sc, 780d2155f2fSWarner Losh LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 781d2155f2fSWarner Losh index = h >> 3; 782d2155f2fSWarner Losh bit = h & 0x1F; 783d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); 784d2155f2fSWarner Losh if (bit > 0xF) 785d2155f2fSWarner Losh bit -= 0x10; 786d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); 787d2155f2fSWarner Losh } 788d2155f2fSWarner Losh IF_ADDR_UNLOCK(ifp); 789d2155f2fSWarner Losh 790d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 791d2155f2fSWarner Losh 792d2155f2fSWarner Losh return; 793d2155f2fSWarner Losh } 794d2155f2fSWarner Losh 795d2155f2fSWarner Losh static void 796d2155f2fSWarner Losh sis_setmulti_sis(struct sis_softc *sc) 797d2155f2fSWarner Losh { 798d2155f2fSWarner Losh struct ifnet *ifp; 799d2155f2fSWarner Losh struct ifmultiaddr *ifma; 800d2155f2fSWarner Losh u_int32_t h, i, n, ctl; 801d2155f2fSWarner Losh u_int16_t hashes[16]; 802d2155f2fSWarner Losh 803d2155f2fSWarner Losh ifp = sc->sis_ifp; 804d2155f2fSWarner Losh 805d2155f2fSWarner Losh /* hash table size */ 806d2155f2fSWarner Losh if (sc->sis_rev >= SIS_REV_635 || 807d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B) 808d2155f2fSWarner Losh n = 16; 809d2155f2fSWarner Losh else 810d2155f2fSWarner Losh n = 8; 811d2155f2fSWarner Losh 812d2155f2fSWarner Losh ctl = CSR_READ_4(sc, SIS_RXFILT_CTL) & SIS_RXFILTCTL_ENABLE; 813d2155f2fSWarner Losh 814d2155f2fSWarner Losh if (ifp->if_flags & IFF_BROADCAST) 815d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_BROAD; 816d2155f2fSWarner Losh 817d2155f2fSWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 818d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_ALLMULTI; 819d2155f2fSWarner Losh if (ifp->if_flags & IFF_PROMISC) 820d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_BROAD|SIS_RXFILTCTL_ALLPHYS; 821d2155f2fSWarner Losh for (i = 0; i < n; i++) 822d2155f2fSWarner Losh hashes[i] = ~0; 823d2155f2fSWarner Losh } else { 824d2155f2fSWarner Losh for (i = 0; i < n; i++) 825d2155f2fSWarner Losh hashes[i] = 0; 826d2155f2fSWarner Losh i = 0; 827d2155f2fSWarner Losh IF_ADDR_LOCK(ifp); 828d2155f2fSWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 829d2155f2fSWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 830d2155f2fSWarner Losh continue; 831d2155f2fSWarner Losh h = sis_mchash(sc, 832d2155f2fSWarner Losh LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 833d2155f2fSWarner Losh hashes[h >> 4] |= 1 << (h & 0xf); 834d2155f2fSWarner Losh i++; 835d2155f2fSWarner Losh } 836d2155f2fSWarner Losh IF_ADDR_UNLOCK(ifp); 837d2155f2fSWarner Losh if (i > n) { 838d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_ALLMULTI; 839d2155f2fSWarner Losh for (i = 0; i < n; i++) 840d2155f2fSWarner Losh hashes[i] = ~0; 841d2155f2fSWarner Losh } 842d2155f2fSWarner Losh } 843d2155f2fSWarner Losh 844d2155f2fSWarner Losh for (i = 0; i < n; i++) { 845d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + i) << 16); 846d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, hashes[i]); 847d2155f2fSWarner Losh } 848d2155f2fSWarner Losh 849d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, ctl); 850d2155f2fSWarner Losh } 851d2155f2fSWarner Losh 852d2155f2fSWarner Losh static void 853d2155f2fSWarner Losh sis_reset(struct sis_softc *sc) 854d2155f2fSWarner Losh { 855d2155f2fSWarner Losh int i; 856d2155f2fSWarner Losh 857d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); 858d2155f2fSWarner Losh 859d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 860d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) 861d2155f2fSWarner Losh break; 862d2155f2fSWarner Losh } 863d2155f2fSWarner Losh 864d2155f2fSWarner Losh if (i == SIS_TIMEOUT) 865d2155f2fSWarner Losh device_printf(sc->sis_dev, "reset never completed\n"); 866d2155f2fSWarner Losh 867d2155f2fSWarner Losh /* Wait a little while for the chip to get its brains in order. */ 868d2155f2fSWarner Losh DELAY(1000); 869d2155f2fSWarner Losh 870d2155f2fSWarner Losh /* 871d2155f2fSWarner Losh * If this is a NetSemi chip, make sure to clear 872d2155f2fSWarner Losh * PME mode. 873d2155f2fSWarner Losh */ 874d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 875d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS); 876d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_CLKRUN, 0); 877d2155f2fSWarner Losh } 878d2155f2fSWarner Losh 879d2155f2fSWarner Losh return; 880d2155f2fSWarner Losh } 881d2155f2fSWarner Losh 882d2155f2fSWarner Losh /* 883d2155f2fSWarner Losh * Probe for an SiS chip. Check the PCI vendor and device 884d2155f2fSWarner Losh * IDs against our list and return a device name if we find a match. 885d2155f2fSWarner Losh */ 886d2155f2fSWarner Losh static int 887d2155f2fSWarner Losh sis_probe(device_t dev) 888d2155f2fSWarner Losh { 889d2155f2fSWarner Losh struct sis_type *t; 890d2155f2fSWarner Losh 891d2155f2fSWarner Losh t = sis_devs; 892d2155f2fSWarner Losh 893d2155f2fSWarner Losh while(t->sis_name != NULL) { 894d2155f2fSWarner Losh if ((pci_get_vendor(dev) == t->sis_vid) && 895d2155f2fSWarner Losh (pci_get_device(dev) == t->sis_did)) { 896d2155f2fSWarner Losh device_set_desc(dev, t->sis_name); 897d2155f2fSWarner Losh return (BUS_PROBE_DEFAULT); 898d2155f2fSWarner Losh } 899d2155f2fSWarner Losh t++; 900d2155f2fSWarner Losh } 901d2155f2fSWarner Losh 902d2155f2fSWarner Losh return(ENXIO); 903d2155f2fSWarner Losh } 904d2155f2fSWarner Losh 905d2155f2fSWarner Losh /* 906d2155f2fSWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 907d2155f2fSWarner Losh * setup and ethernet/BPF attach. 908d2155f2fSWarner Losh */ 909d2155f2fSWarner Losh static int 910d2155f2fSWarner Losh sis_attach(device_t dev) 911d2155f2fSWarner Losh { 912d2155f2fSWarner Losh u_char eaddr[ETHER_ADDR_LEN]; 913d2155f2fSWarner Losh struct sis_softc *sc; 914d2155f2fSWarner Losh struct ifnet *ifp; 915d2155f2fSWarner Losh int error = 0, waittime = 0; 916d2155f2fSWarner Losh 917d2155f2fSWarner Losh waittime = 0; 918d2155f2fSWarner Losh sc = device_get_softc(dev); 919d2155f2fSWarner Losh 920d2155f2fSWarner Losh sc->sis_dev = dev; 921d2155f2fSWarner Losh 922d2155f2fSWarner Losh mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 923d2155f2fSWarner Losh MTX_DEF); 924d2155f2fSWarner Losh callout_init_mtx(&sc->sis_stat_ch, &sc->sis_mtx, 0); 925d2155f2fSWarner Losh 926d2155f2fSWarner Losh if (pci_get_device(dev) == SIS_DEVICEID_900) 927d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_900; 928d2155f2fSWarner Losh if (pci_get_device(dev) == SIS_DEVICEID_7016) 929d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_7016; 930d2155f2fSWarner Losh if (pci_get_vendor(dev) == NS_VENDORID) 931d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_83815; 932d2155f2fSWarner Losh 933d2155f2fSWarner Losh sc->sis_rev = pci_read_config(dev, PCIR_REVID, 1); 934d2155f2fSWarner Losh /* 935d2155f2fSWarner Losh * Map control/status registers. 936d2155f2fSWarner Losh */ 937d2155f2fSWarner Losh pci_enable_busmaster(dev); 938d2155f2fSWarner Losh 939d2155f2fSWarner Losh error = bus_alloc_resources(dev, sis_res_spec, sc->sis_res); 940d2155f2fSWarner Losh if (error) { 941d2155f2fSWarner Losh device_printf(dev, "couldn't allocate resources\n"); 942d2155f2fSWarner Losh goto fail; 943d2155f2fSWarner Losh } 944d2155f2fSWarner Losh 945d2155f2fSWarner Losh /* Reset the adapter. */ 946d2155f2fSWarner Losh sis_reset(sc); 947d2155f2fSWarner Losh 948d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 949d2155f2fSWarner Losh (sc->sis_rev == SIS_REV_635 || 950d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B)) { 951d2155f2fSWarner Losh SIO_SET(SIS_CFG_RND_CNT); 952d2155f2fSWarner Losh SIO_SET(SIS_CFG_PERR_DETECT); 953d2155f2fSWarner Losh } 954d2155f2fSWarner Losh 955d2155f2fSWarner Losh /* 956d2155f2fSWarner Losh * Get station address from the EEPROM. 957d2155f2fSWarner Losh */ 958d2155f2fSWarner Losh switch (pci_get_vendor(dev)) { 959d2155f2fSWarner Losh case NS_VENDORID: 960d2155f2fSWarner Losh sc->sis_srr = CSR_READ_4(sc, NS_SRR); 961d2155f2fSWarner Losh 962d2155f2fSWarner Losh /* We can't update the device description, so spew */ 963d2155f2fSWarner Losh if (sc->sis_srr == NS_SRR_15C) 964d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83815C\n"); 965d2155f2fSWarner Losh else if (sc->sis_srr == NS_SRR_15D) 966d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83815D\n"); 967d2155f2fSWarner Losh else if (sc->sis_srr == NS_SRR_16A) 968d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83816A\n"); 969d2155f2fSWarner Losh else 970d2155f2fSWarner Losh device_printf(dev, "Silicon Revision %x\n", sc->sis_srr); 971d2155f2fSWarner Losh 972d2155f2fSWarner Losh /* 973d2155f2fSWarner Losh * Reading the MAC address out of the EEPROM on 974d2155f2fSWarner Losh * the NatSemi chip takes a bit more work than 975d2155f2fSWarner Losh * you'd expect. The address spans 4 16-bit words, 976d2155f2fSWarner Losh * with the first word containing only a single bit. 977d2155f2fSWarner Losh * You have to shift everything over one bit to 978d2155f2fSWarner Losh * get it aligned properly. Also, the bits are 979d2155f2fSWarner Losh * stored backwards (the LSB is really the MSB, 980d2155f2fSWarner Losh * and so on) so you have to reverse them in order 981d2155f2fSWarner Losh * to get the MAC address into the form we want. 982d2155f2fSWarner Losh * Why? Who the hell knows. 983d2155f2fSWarner Losh */ 984d2155f2fSWarner Losh { 985d2155f2fSWarner Losh u_int16_t tmp[4]; 986d2155f2fSWarner Losh 987d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&tmp, 988d2155f2fSWarner Losh NS_EE_NODEADDR, 4, 0); 989d2155f2fSWarner Losh 990d2155f2fSWarner Losh /* Shift everything over one bit. */ 991d2155f2fSWarner Losh tmp[3] = tmp[3] >> 1; 992d2155f2fSWarner Losh tmp[3] |= tmp[2] << 15; 993d2155f2fSWarner Losh tmp[2] = tmp[2] >> 1; 994d2155f2fSWarner Losh tmp[2] |= tmp[1] << 15; 995d2155f2fSWarner Losh tmp[1] = tmp[1] >> 1; 996d2155f2fSWarner Losh tmp[1] |= tmp[0] << 15; 997d2155f2fSWarner Losh 998d2155f2fSWarner Losh /* Now reverse all the bits. */ 999d2155f2fSWarner Losh tmp[3] = sis_reverse(tmp[3]); 1000d2155f2fSWarner Losh tmp[2] = sis_reverse(tmp[2]); 1001d2155f2fSWarner Losh tmp[1] = sis_reverse(tmp[1]); 1002d2155f2fSWarner Losh 1003d2155f2fSWarner Losh bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN); 1004d2155f2fSWarner Losh } 1005d2155f2fSWarner Losh break; 1006d2155f2fSWarner Losh case SIS_VENDORID: 1007d2155f2fSWarner Losh default: 1008d2155f2fSWarner Losh #if defined(__i386__) || defined(__amd64__) 1009d2155f2fSWarner Losh /* 1010d2155f2fSWarner Losh * If this is a SiS 630E chipset with an embedded 1011d2155f2fSWarner Losh * SiS 900 controller, we have to read the MAC address 1012d2155f2fSWarner Losh * from the APC CMOS RAM. Our method for doing this 1013d2155f2fSWarner Losh * is very ugly since we have to reach out and grab 1014d2155f2fSWarner Losh * ahold of hardware for which we cannot properly 1015d2155f2fSWarner Losh * allocate resources. This code is only compiled on 1016d2155f2fSWarner Losh * the i386 architecture since the SiS 630E chipset 1017d2155f2fSWarner Losh * is for x86 motherboards only. Note that there are 1018d2155f2fSWarner Losh * a lot of magic numbers in this hack. These are 1019d2155f2fSWarner Losh * taken from SiS's Linux driver. I'd like to replace 1020d2155f2fSWarner Losh * them with proper symbolic definitions, but that 1021d2155f2fSWarner Losh * requires some datasheets that I don't have access 1022d2155f2fSWarner Losh * to at the moment. 1023d2155f2fSWarner Losh */ 1024d2155f2fSWarner Losh if (sc->sis_rev == SIS_REV_630S || 1025d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630E || 1026d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630EA1) 1027d2155f2fSWarner Losh sis_read_cmos(sc, dev, (caddr_t)&eaddr, 0x9, 6); 1028d2155f2fSWarner Losh 1029d2155f2fSWarner Losh else if (sc->sis_rev == SIS_REV_635 || 1030d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630ET) 1031d2155f2fSWarner Losh sis_read_mac(sc, dev, (caddr_t)&eaddr); 1032d2155f2fSWarner Losh else if (sc->sis_rev == SIS_REV_96x) { 1033d2155f2fSWarner Losh /* Allow to read EEPROM from LAN. It is shared 1034d2155f2fSWarner Losh * between a 1394 controller and the NIC and each 1035d2155f2fSWarner Losh * time we access it, we need to set SIS_EECMD_REQ. 1036d2155f2fSWarner Losh */ 1037d2155f2fSWarner Losh SIO_SET(SIS_EECMD_REQ); 1038d2155f2fSWarner Losh for (waittime = 0; waittime < SIS_TIMEOUT; 1039d2155f2fSWarner Losh waittime++) { 1040d2155f2fSWarner Losh /* Force EEPROM to idle state. */ 1041d2155f2fSWarner Losh sis_eeprom_idle(sc); 1042d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECMD_GNT) { 1043d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&eaddr, 1044d2155f2fSWarner Losh SIS_EE_NODEADDR, 3, 0); 1045d2155f2fSWarner Losh break; 1046d2155f2fSWarner Losh } 1047d2155f2fSWarner Losh DELAY(1); 1048d2155f2fSWarner Losh } 1049d2155f2fSWarner Losh /* 1050d2155f2fSWarner Losh * Set SIS_EECTL_CLK to high, so a other master 1051d2155f2fSWarner Losh * can operate on the i2c bus. 1052d2155f2fSWarner Losh */ 1053d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 1054d2155f2fSWarner Losh /* Refuse EEPROM access by LAN */ 1055d2155f2fSWarner Losh SIO_SET(SIS_EECMD_DONE); 1056d2155f2fSWarner Losh } else 1057d2155f2fSWarner Losh #endif 1058d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&eaddr, 1059d2155f2fSWarner Losh SIS_EE_NODEADDR, 3, 0); 1060d2155f2fSWarner Losh break; 1061d2155f2fSWarner Losh } 1062d2155f2fSWarner Losh 1063d2155f2fSWarner Losh /* 1064d2155f2fSWarner Losh * Allocate the parent bus DMA tag appropriate for PCI. 1065d2155f2fSWarner Losh */ 1066d2155f2fSWarner Losh #define SIS_NSEG_NEW 32 1067d2155f2fSWarner Losh error = bus_dma_tag_create(NULL, /* parent */ 1068d2155f2fSWarner Losh 1, 0, /* alignment, boundary */ 1069d2155f2fSWarner Losh BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ 1070d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 1071d2155f2fSWarner Losh NULL, NULL, /* filter, filterarg */ 1072d2155f2fSWarner Losh MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */ 1073d2155f2fSWarner Losh BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 1074d2155f2fSWarner Losh BUS_DMA_ALLOCNOW, /* flags */ 1075d2155f2fSWarner Losh NULL, NULL, /* lockfunc, lockarg */ 1076d2155f2fSWarner Losh &sc->sis_parent_tag); 1077d2155f2fSWarner Losh if (error) 1078d2155f2fSWarner Losh goto fail; 1079d2155f2fSWarner Losh 1080d2155f2fSWarner Losh /* 1081d2155f2fSWarner Losh * Now allocate a tag for the DMA descriptor lists and a chunk 1082d2155f2fSWarner Losh * of DMA-able memory based on the tag. Also obtain the physical 1083d2155f2fSWarner Losh * addresses of the RX and TX ring, which we'll need later. 1084d2155f2fSWarner Losh * All of our lists are allocated as a contiguous block 1085d2155f2fSWarner Losh * of memory. 1086d2155f2fSWarner Losh */ 1087d2155f2fSWarner Losh error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1088d2155f2fSWarner Losh 1, 0, /* alignment, boundary */ 1089d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* lowaddr */ 1090d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 1091d2155f2fSWarner Losh NULL, NULL, /* filter, filterarg */ 1092d2155f2fSWarner Losh SIS_RX_LIST_SZ, 1, /* maxsize,nsegments */ 1093d2155f2fSWarner Losh BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 1094d2155f2fSWarner Losh 0, /* flags */ 1095d2155f2fSWarner Losh busdma_lock_mutex, /* lockfunc */ 1096d2155f2fSWarner Losh &Giant, /* lockarg */ 1097d2155f2fSWarner Losh &sc->sis_rx_tag); 1098d2155f2fSWarner Losh if (error) 1099d2155f2fSWarner Losh goto fail; 1100d2155f2fSWarner Losh 1101d2155f2fSWarner Losh error = bus_dmamem_alloc(sc->sis_rx_tag, 1102d2155f2fSWarner Losh (void **)&sc->sis_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, 1103d2155f2fSWarner Losh &sc->sis_rx_dmamap); 1104d2155f2fSWarner Losh 1105d2155f2fSWarner Losh if (error) { 1106d2155f2fSWarner Losh device_printf(dev, "no memory for rx list buffers!\n"); 1107d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_rx_tag); 1108d2155f2fSWarner Losh sc->sis_rx_tag = NULL; 1109d2155f2fSWarner Losh goto fail; 1110d2155f2fSWarner Losh } 1111d2155f2fSWarner Losh 1112d2155f2fSWarner Losh error = bus_dmamap_load(sc->sis_rx_tag, 1113d2155f2fSWarner Losh sc->sis_rx_dmamap, &(sc->sis_rx_list[0]), 1114d2155f2fSWarner Losh sizeof(struct sis_desc), sis_dma_map_ring, 1115d2155f2fSWarner Losh &sc->sis_rx_paddr, 0); 1116d2155f2fSWarner Losh 1117d2155f2fSWarner Losh if (error) { 1118d2155f2fSWarner Losh device_printf(dev, "cannot get address of the rx ring!\n"); 1119d2155f2fSWarner Losh bus_dmamem_free(sc->sis_rx_tag, 1120d2155f2fSWarner Losh sc->sis_rx_list, sc->sis_rx_dmamap); 1121d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_rx_tag); 1122d2155f2fSWarner Losh sc->sis_rx_tag = NULL; 1123d2155f2fSWarner Losh goto fail; 1124d2155f2fSWarner Losh } 1125d2155f2fSWarner Losh 1126d2155f2fSWarner Losh error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1127d2155f2fSWarner Losh 1, 0, /* alignment, boundary */ 1128d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* lowaddr */ 1129d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 1130d2155f2fSWarner Losh NULL, NULL, /* filter, filterarg */ 1131d2155f2fSWarner Losh SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ 1132d2155f2fSWarner Losh BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 1133d2155f2fSWarner Losh 0, /* flags */ 1134d2155f2fSWarner Losh busdma_lock_mutex, /* lockfunc */ 1135d2155f2fSWarner Losh &Giant, /* lockarg */ 1136d2155f2fSWarner Losh &sc->sis_tx_tag); 1137d2155f2fSWarner Losh if (error) 1138d2155f2fSWarner Losh goto fail; 1139d2155f2fSWarner Losh 1140d2155f2fSWarner Losh error = bus_dmamem_alloc(sc->sis_tx_tag, 1141d2155f2fSWarner Losh (void **)&sc->sis_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, 1142d2155f2fSWarner Losh &sc->sis_tx_dmamap); 1143d2155f2fSWarner Losh 1144d2155f2fSWarner Losh if (error) { 1145d2155f2fSWarner Losh device_printf(dev, "no memory for tx list buffers!\n"); 1146d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_tx_tag); 1147d2155f2fSWarner Losh sc->sis_tx_tag = NULL; 1148d2155f2fSWarner Losh goto fail; 1149d2155f2fSWarner Losh } 1150d2155f2fSWarner Losh 1151d2155f2fSWarner Losh error = bus_dmamap_load(sc->sis_tx_tag, 1152d2155f2fSWarner Losh sc->sis_tx_dmamap, &(sc->sis_tx_list[0]), 1153d2155f2fSWarner Losh sizeof(struct sis_desc), sis_dma_map_ring, 1154d2155f2fSWarner Losh &sc->sis_tx_paddr, 0); 1155d2155f2fSWarner Losh 1156d2155f2fSWarner Losh if (error) { 1157d2155f2fSWarner Losh device_printf(dev, "cannot get address of the tx ring!\n"); 1158d2155f2fSWarner Losh bus_dmamem_free(sc->sis_tx_tag, 1159d2155f2fSWarner Losh sc->sis_tx_list, sc->sis_tx_dmamap); 1160d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_tx_tag); 1161d2155f2fSWarner Losh sc->sis_tx_tag = NULL; 1162d2155f2fSWarner Losh goto fail; 1163d2155f2fSWarner Losh } 1164d2155f2fSWarner Losh 1165d2155f2fSWarner Losh error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1166d2155f2fSWarner Losh 1, 0, /* alignment, boundary */ 1167d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* lowaddr */ 1168d2155f2fSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 1169d2155f2fSWarner Losh NULL, NULL, /* filter, filterarg */ 1170d2155f2fSWarner Losh MCLBYTES, 1, /* maxsize,nsegments */ 1171d2155f2fSWarner Losh BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 1172d2155f2fSWarner Losh 0, /* flags */ 1173d2155f2fSWarner Losh busdma_lock_mutex, /* lockfunc */ 1174d2155f2fSWarner Losh &Giant, /* lockarg */ 1175d2155f2fSWarner Losh &sc->sis_tag); 1176d2155f2fSWarner Losh if (error) 1177d2155f2fSWarner Losh goto fail; 1178d2155f2fSWarner Losh 1179d2155f2fSWarner Losh /* 1180d2155f2fSWarner Losh * Obtain the physical addresses of the RX and TX 1181d2155f2fSWarner Losh * rings which we'll need later in the init routine. 1182d2155f2fSWarner Losh */ 1183d2155f2fSWarner Losh 1184d2155f2fSWarner Losh ifp = sc->sis_ifp = if_alloc(IFT_ETHER); 1185d2155f2fSWarner Losh if (ifp == NULL) { 1186d2155f2fSWarner Losh device_printf(dev, "can not if_alloc()\n"); 1187d2155f2fSWarner Losh error = ENOSPC; 1188d2155f2fSWarner Losh goto fail; 1189d2155f2fSWarner Losh } 1190d2155f2fSWarner Losh ifp->if_softc = sc; 1191d2155f2fSWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1192d2155f2fSWarner Losh ifp->if_mtu = ETHERMTU; 1193d2155f2fSWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1194d2155f2fSWarner Losh ifp->if_ioctl = sis_ioctl; 1195d2155f2fSWarner Losh ifp->if_start = sis_start; 1196d2155f2fSWarner Losh ifp->if_init = sis_init; 1197d2155f2fSWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, SIS_TX_LIST_CNT - 1); 1198d2155f2fSWarner Losh ifp->if_snd.ifq_drv_maxlen = SIS_TX_LIST_CNT - 1; 1199d2155f2fSWarner Losh IFQ_SET_READY(&ifp->if_snd); 1200d2155f2fSWarner Losh 1201d2155f2fSWarner Losh /* 1202d2155f2fSWarner Losh * Do MII setup. 1203d2155f2fSWarner Losh */ 1204d2155f2fSWarner Losh if (mii_phy_probe(dev, &sc->sis_miibus, 1205d2155f2fSWarner Losh sis_ifmedia_upd, sis_ifmedia_sts)) { 1206d2155f2fSWarner Losh device_printf(dev, "MII without any PHY!\n"); 1207d2155f2fSWarner Losh error = ENXIO; 1208d2155f2fSWarner Losh goto fail; 1209d2155f2fSWarner Losh } 1210d2155f2fSWarner Losh 1211d2155f2fSWarner Losh /* 1212d2155f2fSWarner Losh * Call MI attach routine. 1213d2155f2fSWarner Losh */ 1214d2155f2fSWarner Losh ether_ifattach(ifp, eaddr); 1215d2155f2fSWarner Losh 1216d2155f2fSWarner Losh /* 1217d2155f2fSWarner Losh * Tell the upper layer(s) we support long frames. 1218d2155f2fSWarner Losh */ 1219d2155f2fSWarner Losh ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 1220d2155f2fSWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 1221d2155f2fSWarner Losh ifp->if_capenable = ifp->if_capabilities; 1222d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1223d2155f2fSWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1224d2155f2fSWarner Losh #endif 1225d2155f2fSWarner Losh 1226d2155f2fSWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1227d2155f2fSWarner Losh error = bus_setup_intr(dev, sc->sis_res[1], INTR_TYPE_NET | INTR_MPSAFE, 1228d2155f2fSWarner Losh NULL, sis_intr, sc, &sc->sis_intrhand); 1229d2155f2fSWarner Losh 1230d2155f2fSWarner Losh if (error) { 1231d2155f2fSWarner Losh device_printf(dev, "couldn't set up irq\n"); 1232d2155f2fSWarner Losh ether_ifdetach(ifp); 1233d2155f2fSWarner Losh goto fail; 1234d2155f2fSWarner Losh } 1235d2155f2fSWarner Losh 1236d2155f2fSWarner Losh fail: 1237d2155f2fSWarner Losh if (error) 1238d2155f2fSWarner Losh sis_detach(dev); 1239d2155f2fSWarner Losh 1240d2155f2fSWarner Losh return(error); 1241d2155f2fSWarner Losh } 1242d2155f2fSWarner Losh 1243d2155f2fSWarner Losh /* 1244d2155f2fSWarner Losh * Shutdown hardware and free up resources. This can be called any 1245d2155f2fSWarner Losh * time after the mutex has been initialized. It is called in both 1246d2155f2fSWarner Losh * the error case in attach and the normal detach case so it needs 1247d2155f2fSWarner Losh * to be careful about only freeing resources that have actually been 1248d2155f2fSWarner Losh * allocated. 1249d2155f2fSWarner Losh */ 1250d2155f2fSWarner Losh static int 1251d2155f2fSWarner Losh sis_detach(device_t dev) 1252d2155f2fSWarner Losh { 1253d2155f2fSWarner Losh struct sis_softc *sc; 1254d2155f2fSWarner Losh struct ifnet *ifp; 1255d2155f2fSWarner Losh 1256d2155f2fSWarner Losh sc = device_get_softc(dev); 1257d2155f2fSWarner Losh KASSERT(mtx_initialized(&sc->sis_mtx), ("sis mutex not initialized")); 1258d2155f2fSWarner Losh ifp = sc->sis_ifp; 1259d2155f2fSWarner Losh 1260d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1261d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1262d2155f2fSWarner Losh ether_poll_deregister(ifp); 1263d2155f2fSWarner Losh #endif 1264d2155f2fSWarner Losh 1265d2155f2fSWarner Losh /* These should only be active if attach succeeded. */ 1266d2155f2fSWarner Losh if (device_is_attached(dev)) { 1267d2155f2fSWarner Losh SIS_LOCK(sc); 1268d2155f2fSWarner Losh sis_reset(sc); 1269d2155f2fSWarner Losh sis_stop(sc); 1270d2155f2fSWarner Losh SIS_UNLOCK(sc); 1271d2155f2fSWarner Losh callout_drain(&sc->sis_stat_ch); 1272d2155f2fSWarner Losh ether_ifdetach(ifp); 1273d2155f2fSWarner Losh } 1274d2155f2fSWarner Losh if (sc->sis_miibus) 1275d2155f2fSWarner Losh device_delete_child(dev, sc->sis_miibus); 1276d2155f2fSWarner Losh bus_generic_detach(dev); 1277d2155f2fSWarner Losh 1278d2155f2fSWarner Losh if (sc->sis_intrhand) 1279d2155f2fSWarner Losh bus_teardown_intr(dev, sc->sis_res[1], sc->sis_intrhand); 1280d2155f2fSWarner Losh bus_release_resources(dev, sis_res_spec, sc->sis_res); 1281d2155f2fSWarner Losh 1282d2155f2fSWarner Losh if (ifp) 1283d2155f2fSWarner Losh if_free(ifp); 1284d2155f2fSWarner Losh 1285d2155f2fSWarner Losh if (sc->sis_rx_tag) { 1286d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_rx_tag, 1287d2155f2fSWarner Losh sc->sis_rx_dmamap); 1288d2155f2fSWarner Losh bus_dmamem_free(sc->sis_rx_tag, 1289d2155f2fSWarner Losh sc->sis_rx_list, sc->sis_rx_dmamap); 1290d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_rx_tag); 1291d2155f2fSWarner Losh } 1292d2155f2fSWarner Losh if (sc->sis_tx_tag) { 1293d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_tx_tag, 1294d2155f2fSWarner Losh sc->sis_tx_dmamap); 1295d2155f2fSWarner Losh bus_dmamem_free(sc->sis_tx_tag, 1296d2155f2fSWarner Losh sc->sis_tx_list, sc->sis_tx_dmamap); 1297d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_tx_tag); 1298d2155f2fSWarner Losh } 1299d2155f2fSWarner Losh if (sc->sis_parent_tag) 1300d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_parent_tag); 1301d2155f2fSWarner Losh if (sc->sis_tag) 1302d2155f2fSWarner Losh bus_dma_tag_destroy(sc->sis_tag); 1303d2155f2fSWarner Losh 1304d2155f2fSWarner Losh mtx_destroy(&sc->sis_mtx); 1305d2155f2fSWarner Losh 1306d2155f2fSWarner Losh return(0); 1307d2155f2fSWarner Losh } 1308d2155f2fSWarner Losh 1309d2155f2fSWarner Losh /* 1310d2155f2fSWarner Losh * Initialize the TX and RX descriptors and allocate mbufs for them. Note that 1311d2155f2fSWarner Losh * we arrange the descriptors in a closed ring, so that the last descriptor 1312d2155f2fSWarner Losh * points back to the first. 1313d2155f2fSWarner Losh */ 1314d2155f2fSWarner Losh static int 1315d2155f2fSWarner Losh sis_ring_init(struct sis_softc *sc) 1316d2155f2fSWarner Losh { 1317d2155f2fSWarner Losh int i, error; 1318d2155f2fSWarner Losh struct sis_desc *dp; 1319d2155f2fSWarner Losh 1320d2155f2fSWarner Losh dp = &sc->sis_tx_list[0]; 1321d2155f2fSWarner Losh for (i = 0; i < SIS_TX_LIST_CNT; i++, dp++) { 1322d2155f2fSWarner Losh if (i == (SIS_TX_LIST_CNT - 1)) 1323d2155f2fSWarner Losh dp->sis_nextdesc = &sc->sis_tx_list[0]; 1324d2155f2fSWarner Losh else 1325d2155f2fSWarner Losh dp->sis_nextdesc = dp + 1; 1326d2155f2fSWarner Losh bus_dmamap_load(sc->sis_tx_tag, 1327d2155f2fSWarner Losh sc->sis_tx_dmamap, 1328d2155f2fSWarner Losh dp->sis_nextdesc, sizeof(struct sis_desc), 1329d2155f2fSWarner Losh sis_dma_map_desc_next, dp, 0); 1330d2155f2fSWarner Losh dp->sis_mbuf = NULL; 1331d2155f2fSWarner Losh dp->sis_ptr = 0; 1332d2155f2fSWarner Losh dp->sis_ctl = 0; 1333d2155f2fSWarner Losh } 1334d2155f2fSWarner Losh 1335d2155f2fSWarner Losh sc->sis_tx_prod = sc->sis_tx_cons = sc->sis_tx_cnt = 0; 1336d2155f2fSWarner Losh 1337d2155f2fSWarner Losh bus_dmamap_sync(sc->sis_tx_tag, 1338d2155f2fSWarner Losh sc->sis_tx_dmamap, BUS_DMASYNC_PREWRITE); 1339d2155f2fSWarner Losh 1340d2155f2fSWarner Losh dp = &sc->sis_rx_list[0]; 1341d2155f2fSWarner Losh for (i = 0; i < SIS_RX_LIST_CNT; i++, dp++) { 1342d2155f2fSWarner Losh error = sis_newbuf(sc, dp, NULL); 1343d2155f2fSWarner Losh if (error) 1344d2155f2fSWarner Losh return(error); 1345d2155f2fSWarner Losh if (i == (SIS_RX_LIST_CNT - 1)) 1346d2155f2fSWarner Losh dp->sis_nextdesc = &sc->sis_rx_list[0]; 1347d2155f2fSWarner Losh else 1348d2155f2fSWarner Losh dp->sis_nextdesc = dp + 1; 1349d2155f2fSWarner Losh bus_dmamap_load(sc->sis_rx_tag, 1350d2155f2fSWarner Losh sc->sis_rx_dmamap, 1351d2155f2fSWarner Losh dp->sis_nextdesc, sizeof(struct sis_desc), 1352d2155f2fSWarner Losh sis_dma_map_desc_next, dp, 0); 1353d2155f2fSWarner Losh } 1354d2155f2fSWarner Losh 1355d2155f2fSWarner Losh bus_dmamap_sync(sc->sis_rx_tag, 1356d2155f2fSWarner Losh sc->sis_rx_dmamap, BUS_DMASYNC_PREWRITE); 1357d2155f2fSWarner Losh 1358d2155f2fSWarner Losh sc->sis_rx_pdsc = &sc->sis_rx_list[0]; 1359d2155f2fSWarner Losh 1360d2155f2fSWarner Losh return(0); 1361d2155f2fSWarner Losh } 1362d2155f2fSWarner Losh 1363d2155f2fSWarner Losh /* 1364d2155f2fSWarner Losh * Initialize an RX descriptor and attach an MBUF cluster. 1365d2155f2fSWarner Losh */ 1366d2155f2fSWarner Losh static int 1367d2155f2fSWarner Losh sis_newbuf(struct sis_softc *sc, struct sis_desc *c, struct mbuf *m) 1368d2155f2fSWarner Losh { 1369d2155f2fSWarner Losh 1370d2155f2fSWarner Losh if (c == NULL) 1371d2155f2fSWarner Losh return(EINVAL); 1372d2155f2fSWarner Losh 1373d2155f2fSWarner Losh if (m == NULL) { 1374d2155f2fSWarner Losh m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1375d2155f2fSWarner Losh if (m == NULL) 1376d2155f2fSWarner Losh return(ENOBUFS); 1377d2155f2fSWarner Losh } else 1378d2155f2fSWarner Losh m->m_data = m->m_ext.ext_buf; 1379d2155f2fSWarner Losh 1380d2155f2fSWarner Losh c->sis_mbuf = m; 1381d2155f2fSWarner Losh c->sis_ctl = SIS_RXLEN; 1382d2155f2fSWarner Losh 1383d2155f2fSWarner Losh bus_dmamap_create(sc->sis_tag, 0, &c->sis_map); 1384d2155f2fSWarner Losh bus_dmamap_load(sc->sis_tag, c->sis_map, 1385d2155f2fSWarner Losh mtod(m, void *), MCLBYTES, 1386d2155f2fSWarner Losh sis_dma_map_desc_ptr, c, 0); 1387d2155f2fSWarner Losh bus_dmamap_sync(sc->sis_tag, c->sis_map, BUS_DMASYNC_PREREAD); 1388d2155f2fSWarner Losh 1389d2155f2fSWarner Losh return(0); 1390d2155f2fSWarner Losh } 1391d2155f2fSWarner Losh 1392d2155f2fSWarner Losh /* 1393d2155f2fSWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 1394d2155f2fSWarner Losh * the higher level protocols. 1395d2155f2fSWarner Losh */ 1396d2155f2fSWarner Losh static void 1397d2155f2fSWarner Losh sis_rxeof(struct sis_softc *sc) 1398d2155f2fSWarner Losh { 1399d2155f2fSWarner Losh struct mbuf *m, *m0; 1400d2155f2fSWarner Losh struct ifnet *ifp; 1401d2155f2fSWarner Losh struct sis_desc *cur_rx; 1402d2155f2fSWarner Losh int total_len = 0; 1403d2155f2fSWarner Losh u_int32_t rxstat; 1404d2155f2fSWarner Losh 1405d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1406d2155f2fSWarner Losh 1407d2155f2fSWarner Losh ifp = sc->sis_ifp; 1408d2155f2fSWarner Losh 1409d2155f2fSWarner Losh for(cur_rx = sc->sis_rx_pdsc; SIS_OWNDESC(cur_rx); 1410d2155f2fSWarner Losh cur_rx = cur_rx->sis_nextdesc) { 1411d2155f2fSWarner Losh 1412d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1413d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 1414d2155f2fSWarner Losh if (sc->rxcycles <= 0) 1415d2155f2fSWarner Losh break; 1416d2155f2fSWarner Losh sc->rxcycles--; 1417d2155f2fSWarner Losh } 1418d2155f2fSWarner Losh #endif 1419d2155f2fSWarner Losh rxstat = cur_rx->sis_rxstat; 1420d2155f2fSWarner Losh bus_dmamap_sync(sc->sis_tag, 1421d2155f2fSWarner Losh cur_rx->sis_map, BUS_DMASYNC_POSTWRITE); 1422d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_tag, cur_rx->sis_map); 1423d2155f2fSWarner Losh bus_dmamap_destroy(sc->sis_tag, cur_rx->sis_map); 1424d2155f2fSWarner Losh m = cur_rx->sis_mbuf; 1425d2155f2fSWarner Losh cur_rx->sis_mbuf = NULL; 1426d2155f2fSWarner Losh total_len = SIS_RXBYTES(cur_rx); 1427d2155f2fSWarner Losh 1428d2155f2fSWarner Losh /* 1429d2155f2fSWarner Losh * If an error occurs, update stats, clear the 1430d2155f2fSWarner Losh * status word and leave the mbuf cluster in place: 1431d2155f2fSWarner Losh * it should simply get re-used next time this descriptor 1432d2155f2fSWarner Losh * comes up in the ring. 1433d2155f2fSWarner Losh */ 1434d2155f2fSWarner Losh if (!(rxstat & SIS_CMDSTS_PKT_OK)) { 1435d2155f2fSWarner Losh ifp->if_ierrors++; 1436d2155f2fSWarner Losh if (rxstat & SIS_RXSTAT_COLL) 1437d2155f2fSWarner Losh ifp->if_collisions++; 1438d2155f2fSWarner Losh sis_newbuf(sc, cur_rx, m); 1439d2155f2fSWarner Losh continue; 1440d2155f2fSWarner Losh } 1441d2155f2fSWarner Losh 1442d2155f2fSWarner Losh /* No errors; receive the packet. */ 1443d2155f2fSWarner Losh #ifdef __NO_STRICT_ALIGNMENT 1444d2155f2fSWarner Losh /* 1445d2155f2fSWarner Losh * On architectures without alignment problems we try to 1446d2155f2fSWarner Losh * allocate a new buffer for the receive ring, and pass up 1447d2155f2fSWarner Losh * the one where the packet is already, saving the expensive 1448d2155f2fSWarner Losh * copy done in m_devget(). 1449d2155f2fSWarner Losh * If we are on an architecture with alignment problems, or 1450d2155f2fSWarner Losh * if the allocation fails, then use m_devget and leave the 1451d2155f2fSWarner Losh * existing buffer in the receive ring. 1452d2155f2fSWarner Losh */ 1453d2155f2fSWarner Losh if (sis_newbuf(sc, cur_rx, NULL) == 0) 1454d2155f2fSWarner Losh m->m_pkthdr.len = m->m_len = total_len; 1455d2155f2fSWarner Losh else 1456d2155f2fSWarner Losh #endif 1457d2155f2fSWarner Losh { 1458d2155f2fSWarner Losh m0 = m_devget(mtod(m, char *), total_len, 1459d2155f2fSWarner Losh ETHER_ALIGN, ifp, NULL); 1460d2155f2fSWarner Losh sis_newbuf(sc, cur_rx, m); 1461d2155f2fSWarner Losh if (m0 == NULL) { 1462d2155f2fSWarner Losh ifp->if_ierrors++; 1463d2155f2fSWarner Losh continue; 1464d2155f2fSWarner Losh } 1465d2155f2fSWarner Losh m = m0; 1466d2155f2fSWarner Losh } 1467d2155f2fSWarner Losh 1468d2155f2fSWarner Losh ifp->if_ipackets++; 1469d2155f2fSWarner Losh m->m_pkthdr.rcvif = ifp; 1470d2155f2fSWarner Losh 1471d2155f2fSWarner Losh SIS_UNLOCK(sc); 1472d2155f2fSWarner Losh (*ifp->if_input)(ifp, m); 1473d2155f2fSWarner Losh SIS_LOCK(sc); 1474d2155f2fSWarner Losh } 1475d2155f2fSWarner Losh 1476d2155f2fSWarner Losh sc->sis_rx_pdsc = cur_rx; 1477d2155f2fSWarner Losh } 1478d2155f2fSWarner Losh 1479d2155f2fSWarner Losh static void 1480d2155f2fSWarner Losh sis_rxeoc(struct sis_softc *sc) 1481d2155f2fSWarner Losh { 1482d2155f2fSWarner Losh 1483d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1484d2155f2fSWarner Losh sis_rxeof(sc); 1485d2155f2fSWarner Losh sis_initl(sc); 1486d2155f2fSWarner Losh } 1487d2155f2fSWarner Losh 1488d2155f2fSWarner Losh /* 1489d2155f2fSWarner Losh * A frame was downloaded to the chip. It's safe for us to clean up 1490d2155f2fSWarner Losh * the list buffers. 1491d2155f2fSWarner Losh */ 1492d2155f2fSWarner Losh 1493d2155f2fSWarner Losh static void 1494d2155f2fSWarner Losh sis_txeof(struct sis_softc *sc) 1495d2155f2fSWarner Losh { 1496d2155f2fSWarner Losh struct ifnet *ifp; 1497d2155f2fSWarner Losh u_int32_t idx; 1498d2155f2fSWarner Losh 1499d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1500d2155f2fSWarner Losh ifp = sc->sis_ifp; 1501d2155f2fSWarner Losh 1502d2155f2fSWarner Losh /* 1503d2155f2fSWarner Losh * Go through our tx list and free mbufs for those 1504d2155f2fSWarner Losh * frames that have been transmitted. 1505d2155f2fSWarner Losh */ 1506d2155f2fSWarner Losh for (idx = sc->sis_tx_cons; sc->sis_tx_cnt > 0; 1507d2155f2fSWarner Losh sc->sis_tx_cnt--, SIS_INC(idx, SIS_TX_LIST_CNT) ) { 1508d2155f2fSWarner Losh struct sis_desc *cur_tx = &sc->sis_tx_list[idx]; 1509d2155f2fSWarner Losh 1510d2155f2fSWarner Losh if (SIS_OWNDESC(cur_tx)) 1511d2155f2fSWarner Losh break; 1512d2155f2fSWarner Losh 1513d2155f2fSWarner Losh if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) 1514d2155f2fSWarner Losh continue; 1515d2155f2fSWarner Losh 1516d2155f2fSWarner Losh if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { 1517d2155f2fSWarner Losh ifp->if_oerrors++; 1518d2155f2fSWarner Losh if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) 1519d2155f2fSWarner Losh ifp->if_collisions++; 1520d2155f2fSWarner Losh if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) 1521d2155f2fSWarner Losh ifp->if_collisions++; 1522d2155f2fSWarner Losh } 1523d2155f2fSWarner Losh 1524d2155f2fSWarner Losh ifp->if_collisions += 1525d2155f2fSWarner Losh (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; 1526d2155f2fSWarner Losh 1527d2155f2fSWarner Losh ifp->if_opackets++; 1528d2155f2fSWarner Losh if (cur_tx->sis_mbuf != NULL) { 1529d2155f2fSWarner Losh m_freem(cur_tx->sis_mbuf); 1530d2155f2fSWarner Losh cur_tx->sis_mbuf = NULL; 1531d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_tag, cur_tx->sis_map); 1532d2155f2fSWarner Losh bus_dmamap_destroy(sc->sis_tag, cur_tx->sis_map); 1533d2155f2fSWarner Losh } 1534d2155f2fSWarner Losh } 1535d2155f2fSWarner Losh 1536d2155f2fSWarner Losh if (idx != sc->sis_tx_cons) { 1537d2155f2fSWarner Losh /* we freed up some buffers */ 1538d2155f2fSWarner Losh sc->sis_tx_cons = idx; 1539d2155f2fSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1540d2155f2fSWarner Losh } 1541d2155f2fSWarner Losh 1542d2155f2fSWarner Losh sc->sis_watchdog_timer = (sc->sis_tx_cnt == 0) ? 0 : 5; 1543d2155f2fSWarner Losh 1544d2155f2fSWarner Losh return; 1545d2155f2fSWarner Losh } 1546d2155f2fSWarner Losh 1547d2155f2fSWarner Losh static void 1548d2155f2fSWarner Losh sis_tick(void *xsc) 1549d2155f2fSWarner Losh { 1550d2155f2fSWarner Losh struct sis_softc *sc; 1551d2155f2fSWarner Losh struct mii_data *mii; 1552d2155f2fSWarner Losh struct ifnet *ifp; 1553d2155f2fSWarner Losh 1554d2155f2fSWarner Losh sc = xsc; 1555d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1556d2155f2fSWarner Losh sc->in_tick = 1; 1557d2155f2fSWarner Losh ifp = sc->sis_ifp; 1558d2155f2fSWarner Losh 1559d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 1560d2155f2fSWarner Losh mii_tick(mii); 1561d2155f2fSWarner Losh 1562d2155f2fSWarner Losh sis_watchdog(sc); 1563d2155f2fSWarner Losh 1564d2155f2fSWarner Losh if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE && 1565d2155f2fSWarner Losh IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 1566d2155f2fSWarner Losh sc->sis_link++; 1567d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1568d2155f2fSWarner Losh sis_startl(ifp); 1569d2155f2fSWarner Losh } 1570d2155f2fSWarner Losh 1571d2155f2fSWarner Losh callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); 1572d2155f2fSWarner Losh sc->in_tick = 0; 1573d2155f2fSWarner Losh } 1574d2155f2fSWarner Losh 1575d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1576d2155f2fSWarner Losh static poll_handler_t sis_poll; 1577d2155f2fSWarner Losh 1578d2155f2fSWarner Losh static void 1579d2155f2fSWarner Losh sis_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 1580d2155f2fSWarner Losh { 1581d2155f2fSWarner Losh struct sis_softc *sc = ifp->if_softc; 1582d2155f2fSWarner Losh 1583d2155f2fSWarner Losh SIS_LOCK(sc); 1584d2155f2fSWarner Losh if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1585d2155f2fSWarner Losh SIS_UNLOCK(sc); 1586d2155f2fSWarner Losh return; 1587d2155f2fSWarner Losh } 1588d2155f2fSWarner Losh 1589d2155f2fSWarner Losh /* 1590d2155f2fSWarner Losh * On the sis, reading the status register also clears it. 1591d2155f2fSWarner Losh * So before returning to intr mode we must make sure that all 1592d2155f2fSWarner Losh * possible pending sources of interrupts have been served. 1593d2155f2fSWarner Losh * In practice this means run to completion the *eof routines, 1594d2155f2fSWarner Losh * and then call the interrupt routine 1595d2155f2fSWarner Losh */ 1596d2155f2fSWarner Losh sc->rxcycles = count; 1597d2155f2fSWarner Losh sis_rxeof(sc); 1598d2155f2fSWarner Losh sis_txeof(sc); 1599d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1600d2155f2fSWarner Losh sis_startl(ifp); 1601d2155f2fSWarner Losh 1602d2155f2fSWarner Losh if (sc->rxcycles > 0 || cmd == POLL_AND_CHECK_STATUS) { 1603d2155f2fSWarner Losh u_int32_t status; 1604d2155f2fSWarner Losh 1605d2155f2fSWarner Losh /* Reading the ISR register clears all interrupts. */ 1606d2155f2fSWarner Losh status = CSR_READ_4(sc, SIS_ISR); 1607d2155f2fSWarner Losh 1608d2155f2fSWarner Losh if (status & (SIS_ISR_RX_ERR|SIS_ISR_RX_OFLOW)) 1609d2155f2fSWarner Losh sis_rxeoc(sc); 1610d2155f2fSWarner Losh 1611d2155f2fSWarner Losh if (status & (SIS_ISR_RX_IDLE)) 1612d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 1613d2155f2fSWarner Losh 1614d2155f2fSWarner Losh if (status & SIS_ISR_SYSERR) { 1615d2155f2fSWarner Losh sis_reset(sc); 1616d2155f2fSWarner Losh sis_initl(sc); 1617d2155f2fSWarner Losh } 1618d2155f2fSWarner Losh } 1619d2155f2fSWarner Losh 1620d2155f2fSWarner Losh SIS_UNLOCK(sc); 1621d2155f2fSWarner Losh } 1622d2155f2fSWarner Losh #endif /* DEVICE_POLLING */ 1623d2155f2fSWarner Losh 1624d2155f2fSWarner Losh static void 1625d2155f2fSWarner Losh sis_intr(void *arg) 1626d2155f2fSWarner Losh { 1627d2155f2fSWarner Losh struct sis_softc *sc; 1628d2155f2fSWarner Losh struct ifnet *ifp; 1629d2155f2fSWarner Losh u_int32_t status; 1630d2155f2fSWarner Losh 1631d2155f2fSWarner Losh sc = arg; 1632d2155f2fSWarner Losh ifp = sc->sis_ifp; 1633d2155f2fSWarner Losh 1634d2155f2fSWarner Losh if (sc->sis_stopped) /* Most likely shared interrupt */ 1635d2155f2fSWarner Losh return; 1636d2155f2fSWarner Losh 1637d2155f2fSWarner Losh SIS_LOCK(sc); 1638d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1639d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 1640d2155f2fSWarner Losh SIS_UNLOCK(sc); 1641d2155f2fSWarner Losh return; 1642d2155f2fSWarner Losh } 1643d2155f2fSWarner Losh #endif 1644d2155f2fSWarner Losh 1645d2155f2fSWarner Losh /* Disable interrupts. */ 1646d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 1647d2155f2fSWarner Losh 1648d2155f2fSWarner Losh for (;;) { 1649d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1650d2155f2fSWarner Losh /* Reading the ISR register clears all interrupts. */ 1651d2155f2fSWarner Losh status = CSR_READ_4(sc, SIS_ISR); 1652d2155f2fSWarner Losh 1653d2155f2fSWarner Losh if ((status & SIS_INTRS) == 0) 1654d2155f2fSWarner Losh break; 1655d2155f2fSWarner Losh 1656d2155f2fSWarner Losh if (status & 1657d2155f2fSWarner Losh (SIS_ISR_TX_DESC_OK | SIS_ISR_TX_ERR | 1658d2155f2fSWarner Losh SIS_ISR_TX_OK | SIS_ISR_TX_IDLE) ) 1659d2155f2fSWarner Losh sis_txeof(sc); 1660d2155f2fSWarner Losh 1661d2155f2fSWarner Losh if (status & (SIS_ISR_RX_DESC_OK|SIS_ISR_RX_OK|SIS_ISR_RX_IDLE)) 1662d2155f2fSWarner Losh sis_rxeof(sc); 1663d2155f2fSWarner Losh 1664d2155f2fSWarner Losh if (status & (SIS_ISR_RX_ERR | SIS_ISR_RX_OFLOW)) 1665d2155f2fSWarner Losh sis_rxeoc(sc); 1666d2155f2fSWarner Losh 1667d2155f2fSWarner Losh if (status & (SIS_ISR_RX_IDLE)) 1668d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 1669d2155f2fSWarner Losh 1670d2155f2fSWarner Losh if (status & SIS_ISR_SYSERR) { 1671d2155f2fSWarner Losh sis_reset(sc); 1672d2155f2fSWarner Losh sis_initl(sc); 1673d2155f2fSWarner Losh } 1674d2155f2fSWarner Losh } 1675d2155f2fSWarner Losh 1676d2155f2fSWarner Losh /* Re-enable interrupts. */ 1677d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 1678d2155f2fSWarner Losh 1679d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1680d2155f2fSWarner Losh sis_startl(ifp); 1681d2155f2fSWarner Losh 1682d2155f2fSWarner Losh SIS_UNLOCK(sc); 1683d2155f2fSWarner Losh } 1684d2155f2fSWarner Losh 1685d2155f2fSWarner Losh /* 1686d2155f2fSWarner Losh * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 1687d2155f2fSWarner Losh * pointers to the fragment pointers. 1688d2155f2fSWarner Losh */ 1689d2155f2fSWarner Losh static int 1690d2155f2fSWarner Losh sis_encap(struct sis_softc *sc, struct mbuf **m_head, uint32_t *txidx) 1691d2155f2fSWarner Losh { 1692d2155f2fSWarner Losh struct sis_desc *f = NULL; 1693d2155f2fSWarner Losh struct mbuf *m; 1694d2155f2fSWarner Losh int frag, cur, cnt = 0, chainlen = 0; 1695d2155f2fSWarner Losh 1696d2155f2fSWarner Losh /* 1697d2155f2fSWarner Losh * If there's no way we can send any packets, return now. 1698d2155f2fSWarner Losh */ 1699d2155f2fSWarner Losh if (SIS_TX_LIST_CNT - sc->sis_tx_cnt < 2) 1700d2155f2fSWarner Losh return (ENOBUFS); 1701d2155f2fSWarner Losh 1702d2155f2fSWarner Losh /* 1703d2155f2fSWarner Losh * Count the number of frags in this chain to see if 1704d2155f2fSWarner Losh * we need to m_defrag. Since the descriptor list is shared 1705d2155f2fSWarner Losh * by all packets, we'll m_defrag long chains so that they 1706d2155f2fSWarner Losh * do not use up the entire list, even if they would fit. 1707d2155f2fSWarner Losh */ 1708d2155f2fSWarner Losh 1709d2155f2fSWarner Losh for (m = *m_head; m != NULL; m = m->m_next) 1710d2155f2fSWarner Losh chainlen++; 1711d2155f2fSWarner Losh 1712d2155f2fSWarner Losh if ((chainlen > SIS_TX_LIST_CNT / 4) || 1713d2155f2fSWarner Losh ((SIS_TX_LIST_CNT - (chainlen + sc->sis_tx_cnt)) < 2)) { 1714d2155f2fSWarner Losh m = m_defrag(*m_head, M_DONTWAIT); 1715d2155f2fSWarner Losh if (m == NULL) 1716d2155f2fSWarner Losh return (ENOBUFS); 1717d2155f2fSWarner Losh *m_head = m; 1718d2155f2fSWarner Losh } 1719d2155f2fSWarner Losh 1720d2155f2fSWarner Losh /* 1721d2155f2fSWarner Losh * Start packing the mbufs in this chain into 1722d2155f2fSWarner Losh * the fragment pointers. Stop when we run out 1723d2155f2fSWarner Losh * of fragments or hit the end of the mbuf chain. 1724d2155f2fSWarner Losh */ 1725d2155f2fSWarner Losh cur = frag = *txidx; 1726d2155f2fSWarner Losh 1727d2155f2fSWarner Losh for (m = *m_head; m != NULL; m = m->m_next) { 1728d2155f2fSWarner Losh if (m->m_len != 0) { 1729d2155f2fSWarner Losh if ((SIS_TX_LIST_CNT - 1730d2155f2fSWarner Losh (sc->sis_tx_cnt + cnt)) < 2) 1731d2155f2fSWarner Losh return(ENOBUFS); 1732d2155f2fSWarner Losh f = &sc->sis_tx_list[frag]; 1733d2155f2fSWarner Losh f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; 1734d2155f2fSWarner Losh bus_dmamap_create(sc->sis_tag, 0, &f->sis_map); 1735d2155f2fSWarner Losh bus_dmamap_load(sc->sis_tag, f->sis_map, 1736d2155f2fSWarner Losh mtod(m, void *), m->m_len, 1737d2155f2fSWarner Losh sis_dma_map_desc_ptr, f, 0); 1738d2155f2fSWarner Losh bus_dmamap_sync(sc->sis_tag, 1739d2155f2fSWarner Losh f->sis_map, BUS_DMASYNC_PREREAD); 1740d2155f2fSWarner Losh if (cnt != 0) 1741d2155f2fSWarner Losh f->sis_ctl |= SIS_CMDSTS_OWN; 1742d2155f2fSWarner Losh cur = frag; 1743d2155f2fSWarner Losh SIS_INC(frag, SIS_TX_LIST_CNT); 1744d2155f2fSWarner Losh cnt++; 1745d2155f2fSWarner Losh } 1746d2155f2fSWarner Losh } 1747d2155f2fSWarner Losh 1748d2155f2fSWarner Losh if (m != NULL) 1749d2155f2fSWarner Losh return(ENOBUFS); 1750d2155f2fSWarner Losh 1751d2155f2fSWarner Losh sc->sis_tx_list[cur].sis_mbuf = *m_head; 1752d2155f2fSWarner Losh sc->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; 1753d2155f2fSWarner Losh sc->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; 1754d2155f2fSWarner Losh sc->sis_tx_cnt += cnt; 1755d2155f2fSWarner Losh *txidx = frag; 1756d2155f2fSWarner Losh 1757d2155f2fSWarner Losh return(0); 1758d2155f2fSWarner Losh } 1759d2155f2fSWarner Losh 1760d2155f2fSWarner Losh /* 1761d2155f2fSWarner Losh * Main transmit routine. To avoid having to do mbuf copies, we put pointers 1762d2155f2fSWarner Losh * to the mbuf data regions directly in the transmit lists. We also save a 1763d2155f2fSWarner Losh * copy of the pointers since the transmit list fragment pointers are 1764d2155f2fSWarner Losh * physical addresses. 1765d2155f2fSWarner Losh */ 1766d2155f2fSWarner Losh 1767d2155f2fSWarner Losh static void 1768d2155f2fSWarner Losh sis_start(struct ifnet *ifp) 1769d2155f2fSWarner Losh { 1770d2155f2fSWarner Losh struct sis_softc *sc; 1771d2155f2fSWarner Losh 1772d2155f2fSWarner Losh sc = ifp->if_softc; 1773d2155f2fSWarner Losh SIS_LOCK(sc); 1774d2155f2fSWarner Losh sis_startl(ifp); 1775d2155f2fSWarner Losh SIS_UNLOCK(sc); 1776d2155f2fSWarner Losh } 1777d2155f2fSWarner Losh 1778d2155f2fSWarner Losh static void 1779d2155f2fSWarner Losh sis_startl(struct ifnet *ifp) 1780d2155f2fSWarner Losh { 1781d2155f2fSWarner Losh struct sis_softc *sc; 1782d2155f2fSWarner Losh struct mbuf *m_head = NULL; 1783d2155f2fSWarner Losh u_int32_t idx, queued = 0; 1784d2155f2fSWarner Losh 1785d2155f2fSWarner Losh sc = ifp->if_softc; 1786d2155f2fSWarner Losh 1787d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1788d2155f2fSWarner Losh 1789d2155f2fSWarner Losh if (!sc->sis_link) 1790d2155f2fSWarner Losh return; 1791d2155f2fSWarner Losh 1792d2155f2fSWarner Losh idx = sc->sis_tx_prod; 1793d2155f2fSWarner Losh 1794d2155f2fSWarner Losh if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 1795d2155f2fSWarner Losh return; 1796d2155f2fSWarner Losh 1797d2155f2fSWarner Losh while(sc->sis_tx_list[idx].sis_mbuf == NULL) { 1798d2155f2fSWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 1799d2155f2fSWarner Losh if (m_head == NULL) 1800d2155f2fSWarner Losh break; 1801d2155f2fSWarner Losh 1802d2155f2fSWarner Losh if (sis_encap(sc, &m_head, &idx)) { 1803d2155f2fSWarner Losh IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 1804d2155f2fSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1805d2155f2fSWarner Losh break; 1806d2155f2fSWarner Losh } 1807d2155f2fSWarner Losh 1808d2155f2fSWarner Losh queued++; 1809d2155f2fSWarner Losh 1810d2155f2fSWarner Losh /* 1811d2155f2fSWarner Losh * If there's a BPF listener, bounce a copy of this frame 1812d2155f2fSWarner Losh * to him. 1813d2155f2fSWarner Losh */ 1814d2155f2fSWarner Losh BPF_MTAP(ifp, m_head); 1815d2155f2fSWarner Losh 1816d2155f2fSWarner Losh } 1817d2155f2fSWarner Losh 1818d2155f2fSWarner Losh if (queued) { 1819d2155f2fSWarner Losh /* Transmit */ 1820d2155f2fSWarner Losh sc->sis_tx_prod = idx; 1821d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); 1822d2155f2fSWarner Losh 1823d2155f2fSWarner Losh /* 1824d2155f2fSWarner Losh * Set a timeout in case the chip goes out to lunch. 1825d2155f2fSWarner Losh */ 1826d2155f2fSWarner Losh sc->sis_watchdog_timer = 5; 1827d2155f2fSWarner Losh } 1828d2155f2fSWarner Losh } 1829d2155f2fSWarner Losh 1830d2155f2fSWarner Losh static void 1831d2155f2fSWarner Losh sis_init(void *xsc) 1832d2155f2fSWarner Losh { 1833d2155f2fSWarner Losh struct sis_softc *sc = xsc; 1834d2155f2fSWarner Losh 1835d2155f2fSWarner Losh SIS_LOCK(sc); 1836d2155f2fSWarner Losh sis_initl(sc); 1837d2155f2fSWarner Losh SIS_UNLOCK(sc); 1838d2155f2fSWarner Losh } 1839d2155f2fSWarner Losh 1840d2155f2fSWarner Losh static void 1841d2155f2fSWarner Losh sis_initl(struct sis_softc *sc) 1842d2155f2fSWarner Losh { 1843d2155f2fSWarner Losh struct ifnet *ifp = sc->sis_ifp; 1844d2155f2fSWarner Losh struct mii_data *mii; 1845d2155f2fSWarner Losh 1846d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1847d2155f2fSWarner Losh 1848d2155f2fSWarner Losh /* 1849d2155f2fSWarner Losh * Cancel pending I/O and free all RX/TX buffers. 1850d2155f2fSWarner Losh */ 1851d2155f2fSWarner Losh sis_stop(sc); 1852d2155f2fSWarner Losh sc->sis_stopped = 0; 1853d2155f2fSWarner Losh 1854d2155f2fSWarner Losh #ifdef notyet 1855d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr >= NS_SRR_16A) { 1856d2155f2fSWarner Losh /* 1857d2155f2fSWarner Losh * Configure 400usec of interrupt holdoff. This is based 1858d2155f2fSWarner Losh * on emperical tests on a Soekris 4801. 1859d2155f2fSWarner Losh */ 1860d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_IHR, 0x100 | 4); 1861d2155f2fSWarner Losh } 1862d2155f2fSWarner Losh #endif 1863d2155f2fSWarner Losh 1864d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 1865d2155f2fSWarner Losh 1866d2155f2fSWarner Losh /* Set MAC address */ 1867d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 1868d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); 1869d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1870d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[0]); 1871d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); 1872d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1873d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[1]); 1874d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); 1875d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1876d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[2]); 1877d2155f2fSWarner Losh } else { 1878d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 1879d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1880d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[0]); 1881d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); 1882d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1883d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[1]); 1884d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 1885d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 1886d2155f2fSWarner Losh ((u_int16_t *)IF_LLADDR(sc->sis_ifp))[2]); 1887d2155f2fSWarner Losh } 1888d2155f2fSWarner Losh 1889d2155f2fSWarner Losh /* Init circular TX/RX lists. */ 1890d2155f2fSWarner Losh if (sis_ring_init(sc) != 0) { 1891d2155f2fSWarner Losh device_printf(sc->sis_dev, 1892d2155f2fSWarner Losh "initialization failed: no memory for rx buffers\n"); 1893d2155f2fSWarner Losh sis_stop(sc); 1894d2155f2fSWarner Losh return; 1895d2155f2fSWarner Losh } 1896d2155f2fSWarner Losh 1897d2155f2fSWarner Losh /* 1898d2155f2fSWarner Losh * Short Cable Receive Errors (MP21.E) 1899d2155f2fSWarner Losh * also: Page 78 of the DP83815 data sheet (september 2002 version) 1900d2155f2fSWarner Losh * recommends the following register settings "for optimum 1901d2155f2fSWarner Losh * performance." for rev 15C. Set this also for 15D parts as 1902d2155f2fSWarner Losh * they require it in practice. 1903d2155f2fSWarner Losh */ 1904d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr <= NS_SRR_15D) { 1905d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); 1906d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_CR, 0x189C); 1907d2155f2fSWarner Losh /* set val for c2 */ 1908d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000); 1909d2155f2fSWarner Losh /* load/kill c2 */ 1910d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040); 1911d2155f2fSWarner Losh /* rais SD off, from 4 to c */ 1912d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C); 1913d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0); 1914d2155f2fSWarner Losh } 1915d2155f2fSWarner Losh 1916d2155f2fSWarner Losh 1917d2155f2fSWarner Losh /* 1918d2155f2fSWarner Losh * For the NatSemi chip, we have to explicitly enable the 1919d2155f2fSWarner Losh * reception of ARP frames, as well as turn on the 'perfect 1920d2155f2fSWarner Losh * match' filter where we store the station address, otherwise 1921d2155f2fSWarner Losh * we won't receive unicasts meant for this host. 1922d2155f2fSWarner Losh */ 1923d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 1924d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); 1925d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); 1926d2155f2fSWarner Losh } 1927d2155f2fSWarner Losh 1928d2155f2fSWarner Losh /* If we want promiscuous mode, set the allframes bit. */ 1929d2155f2fSWarner Losh if (ifp->if_flags & IFF_PROMISC) { 1930d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 1931d2155f2fSWarner Losh } else { 1932d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 1933d2155f2fSWarner Losh } 1934d2155f2fSWarner Losh 1935d2155f2fSWarner Losh /* 1936d2155f2fSWarner Losh * Set the capture broadcast bit to capture broadcast frames. 1937d2155f2fSWarner Losh */ 1938d2155f2fSWarner Losh if (ifp->if_flags & IFF_BROADCAST) { 1939d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 1940d2155f2fSWarner Losh } else { 1941d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 1942d2155f2fSWarner Losh } 1943d2155f2fSWarner Losh 1944d2155f2fSWarner Losh /* 1945d2155f2fSWarner Losh * Load the multicast filter. 1946d2155f2fSWarner Losh */ 1947d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 1948d2155f2fSWarner Losh sis_setmulti_ns(sc); 1949d2155f2fSWarner Losh else 1950d2155f2fSWarner Losh sis_setmulti_sis(sc); 1951d2155f2fSWarner Losh 1952d2155f2fSWarner Losh /* Turn the receive filter on */ 1953d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); 1954d2155f2fSWarner Losh 1955d2155f2fSWarner Losh /* 1956d2155f2fSWarner Losh * Load the address of the RX and TX lists. 1957d2155f2fSWarner Losh */ 1958d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_LISTPTR, sc->sis_rx_paddr); 1959d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_LISTPTR, sc->sis_tx_paddr); 1960d2155f2fSWarner Losh 1961d2155f2fSWarner Losh /* SIS_CFG_EDB_MASTER_EN indicates the EDB bus is used instead of 1962d2155f2fSWarner Losh * the PCI bus. When this bit is set, the Max DMA Burst Size 1963d2155f2fSWarner Losh * for TX/RX DMA should be no larger than 16 double words. 1964d2155f2fSWarner Losh */ 1965d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_CFG) & SIS_CFG_EDB_MASTER_EN) { 1966d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG64); 1967d2155f2fSWarner Losh } else { 1968d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG256); 1969d2155f2fSWarner Losh } 1970d2155f2fSWarner Losh 1971d2155f2fSWarner Losh /* Accept Long Packets for VLAN support */ 1972d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_JABBER); 1973d2155f2fSWarner Losh 1974d2155f2fSWarner Losh /* Set TX configuration */ 1975d2155f2fSWarner Losh if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 1976d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10); 1977d2155f2fSWarner Losh } else { 1978d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); 1979d2155f2fSWarner Losh } 1980d2155f2fSWarner Losh 1981d2155f2fSWarner Losh /* Set full/half duplex mode. */ 1982d2155f2fSWarner Losh if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 1983d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_TX_CFG, 1984d2155f2fSWarner Losh (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 1985d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 1986d2155f2fSWarner Losh } else { 1987d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_TX_CFG, 1988d2155f2fSWarner Losh (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 1989d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 1990d2155f2fSWarner Losh } 1991d2155f2fSWarner Losh 1992d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83816) { 1993d2155f2fSWarner Losh /* 1994d2155f2fSWarner Losh * MPII03.D: Half Duplex Excessive Collisions. 1995d2155f2fSWarner Losh * Also page 49 in 83816 manual 1996d2155f2fSWarner Losh */ 1997d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_TX_CFG, SIS_TXCFG_MPII03D); 1998d2155f2fSWarner Losh } 1999d2155f2fSWarner Losh 2000d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr < NS_SRR_16A && 2001d2155f2fSWarner Losh IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 2002d2155f2fSWarner Losh uint32_t reg; 2003d2155f2fSWarner Losh 2004d2155f2fSWarner Losh /* 2005d2155f2fSWarner Losh * Short Cable Receive Errors (MP21.E) 2006d2155f2fSWarner Losh */ 2007d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); 2008d2155f2fSWarner Losh reg = CSR_READ_4(sc, NS_PHY_DSPCFG) & 0xfff; 2009d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_DSPCFG, reg | 0x1000); 2010d2155f2fSWarner Losh DELAY(100000); 2011d2155f2fSWarner Losh reg = CSR_READ_4(sc, NS_PHY_TDATA) & 0xff; 2012d2155f2fSWarner Losh if ((reg & 0x0080) == 0 || (reg > 0xd8 && reg <= 0xff)) { 2013d2155f2fSWarner Losh device_printf(sc->sis_dev, 2014d2155f2fSWarner Losh "Applying short cable fix (reg=%x)\n", reg); 2015d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_TDATA, 0x00e8); 2016d2155f2fSWarner Losh SIS_SETBIT(sc, NS_PHY_DSPCFG, 0x20); 2017d2155f2fSWarner Losh } 2018d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0); 2019d2155f2fSWarner Losh } 2020d2155f2fSWarner Losh 2021d2155f2fSWarner Losh /* 2022d2155f2fSWarner Losh * Enable interrupts. 2023d2155f2fSWarner Losh */ 2024d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); 2025d2155f2fSWarner Losh #ifdef DEVICE_POLLING 2026d2155f2fSWarner Losh /* 2027d2155f2fSWarner Losh * ... only enable interrupts if we are not polling, make sure 2028d2155f2fSWarner Losh * they are off otherwise. 2029d2155f2fSWarner Losh */ 2030d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 2031d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 2032d2155f2fSWarner Losh else 2033d2155f2fSWarner Losh #endif 2034d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 2035d2155f2fSWarner Losh 2036d2155f2fSWarner Losh /* Enable receiver and transmitter. */ 2037d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 2038d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 2039d2155f2fSWarner Losh 2040d2155f2fSWarner Losh #ifdef notdef 2041d2155f2fSWarner Losh mii_mediachg(mii); 2042d2155f2fSWarner Losh #endif 2043d2155f2fSWarner Losh 2044d2155f2fSWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 2045d2155f2fSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2046d2155f2fSWarner Losh 2047d2155f2fSWarner Losh if (!sc->in_tick) 2048d2155f2fSWarner Losh callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); 2049d2155f2fSWarner Losh } 2050d2155f2fSWarner Losh 2051d2155f2fSWarner Losh /* 2052d2155f2fSWarner Losh * Set media options. 2053d2155f2fSWarner Losh */ 2054d2155f2fSWarner Losh static int 2055d2155f2fSWarner Losh sis_ifmedia_upd(struct ifnet *ifp) 2056d2155f2fSWarner Losh { 2057d2155f2fSWarner Losh struct sis_softc *sc; 2058d2155f2fSWarner Losh struct mii_data *mii; 2059d2155f2fSWarner Losh 2060d2155f2fSWarner Losh sc = ifp->if_softc; 2061d2155f2fSWarner Losh 2062d2155f2fSWarner Losh SIS_LOCK(sc); 2063d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2064d2155f2fSWarner Losh sc->sis_link = 0; 2065d2155f2fSWarner Losh if (mii->mii_instance) { 2066d2155f2fSWarner Losh struct mii_softc *miisc; 2067d2155f2fSWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 2068d2155f2fSWarner Losh mii_phy_reset(miisc); 2069d2155f2fSWarner Losh } 2070d2155f2fSWarner Losh mii_mediachg(mii); 2071d2155f2fSWarner Losh SIS_UNLOCK(sc); 2072d2155f2fSWarner Losh 2073d2155f2fSWarner Losh return(0); 2074d2155f2fSWarner Losh } 2075d2155f2fSWarner Losh 2076d2155f2fSWarner Losh /* 2077d2155f2fSWarner Losh * Report current media status. 2078d2155f2fSWarner Losh */ 2079d2155f2fSWarner Losh static void 2080d2155f2fSWarner Losh sis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 2081d2155f2fSWarner Losh { 2082d2155f2fSWarner Losh struct sis_softc *sc; 2083d2155f2fSWarner Losh struct mii_data *mii; 2084d2155f2fSWarner Losh 2085d2155f2fSWarner Losh sc = ifp->if_softc; 2086d2155f2fSWarner Losh 2087d2155f2fSWarner Losh SIS_LOCK(sc); 2088d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2089d2155f2fSWarner Losh mii_pollstat(mii); 2090d2155f2fSWarner Losh SIS_UNLOCK(sc); 2091d2155f2fSWarner Losh ifmr->ifm_active = mii->mii_media_active; 2092d2155f2fSWarner Losh ifmr->ifm_status = mii->mii_media_status; 2093d2155f2fSWarner Losh } 2094d2155f2fSWarner Losh 2095d2155f2fSWarner Losh static int 2096d2155f2fSWarner Losh sis_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2097d2155f2fSWarner Losh { 2098d2155f2fSWarner Losh struct sis_softc *sc = ifp->if_softc; 2099d2155f2fSWarner Losh struct ifreq *ifr = (struct ifreq *) data; 2100d2155f2fSWarner Losh struct mii_data *mii; 2101d2155f2fSWarner Losh int error = 0; 2102d2155f2fSWarner Losh 2103d2155f2fSWarner Losh switch(command) { 2104d2155f2fSWarner Losh case SIOCSIFFLAGS: 2105d2155f2fSWarner Losh SIS_LOCK(sc); 2106d2155f2fSWarner Losh if (ifp->if_flags & IFF_UP) { 2107d2155f2fSWarner Losh sis_initl(sc); 2108d2155f2fSWarner Losh } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2109d2155f2fSWarner Losh sis_stop(sc); 2110d2155f2fSWarner Losh } 2111d2155f2fSWarner Losh SIS_UNLOCK(sc); 2112d2155f2fSWarner Losh error = 0; 2113d2155f2fSWarner Losh break; 2114d2155f2fSWarner Losh case SIOCADDMULTI: 2115d2155f2fSWarner Losh case SIOCDELMULTI: 2116d2155f2fSWarner Losh SIS_LOCK(sc); 2117d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 2118d2155f2fSWarner Losh sis_setmulti_ns(sc); 2119d2155f2fSWarner Losh else 2120d2155f2fSWarner Losh sis_setmulti_sis(sc); 2121d2155f2fSWarner Losh SIS_UNLOCK(sc); 2122d2155f2fSWarner Losh error = 0; 2123d2155f2fSWarner Losh break; 2124d2155f2fSWarner Losh case SIOCGIFMEDIA: 2125d2155f2fSWarner Losh case SIOCSIFMEDIA: 2126d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2127d2155f2fSWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 2128d2155f2fSWarner Losh break; 2129d2155f2fSWarner Losh case SIOCSIFCAP: 2130d2155f2fSWarner Losh /* ok, disable interrupts */ 2131d2155f2fSWarner Losh #ifdef DEVICE_POLLING 2132d2155f2fSWarner Losh if (ifr->ifr_reqcap & IFCAP_POLLING && 2133d2155f2fSWarner Losh !(ifp->if_capenable & IFCAP_POLLING)) { 2134d2155f2fSWarner Losh error = ether_poll_register(sis_poll, ifp); 2135d2155f2fSWarner Losh if (error) 2136d2155f2fSWarner Losh return(error); 2137d2155f2fSWarner Losh SIS_LOCK(sc); 2138d2155f2fSWarner Losh /* Disable interrupts */ 2139d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 2140d2155f2fSWarner Losh ifp->if_capenable |= IFCAP_POLLING; 2141d2155f2fSWarner Losh SIS_UNLOCK(sc); 2142d2155f2fSWarner Losh return (error); 2143d2155f2fSWarner Losh 2144d2155f2fSWarner Losh } 2145d2155f2fSWarner Losh if (!(ifr->ifr_reqcap & IFCAP_POLLING) && 2146d2155f2fSWarner Losh ifp->if_capenable & IFCAP_POLLING) { 2147d2155f2fSWarner Losh error = ether_poll_deregister(ifp); 2148d2155f2fSWarner Losh /* Enable interrupts. */ 2149d2155f2fSWarner Losh SIS_LOCK(sc); 2150d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 2151d2155f2fSWarner Losh ifp->if_capenable &= ~IFCAP_POLLING; 2152d2155f2fSWarner Losh SIS_UNLOCK(sc); 2153d2155f2fSWarner Losh return (error); 2154d2155f2fSWarner Losh } 2155d2155f2fSWarner Losh #endif /* DEVICE_POLLING */ 2156d2155f2fSWarner Losh break; 2157d2155f2fSWarner Losh default: 2158d2155f2fSWarner Losh error = ether_ioctl(ifp, command, data); 2159d2155f2fSWarner Losh break; 2160d2155f2fSWarner Losh } 2161d2155f2fSWarner Losh 2162d2155f2fSWarner Losh return(error); 2163d2155f2fSWarner Losh } 2164d2155f2fSWarner Losh 2165d2155f2fSWarner Losh static void 2166d2155f2fSWarner Losh sis_watchdog(struct sis_softc *sc) 2167d2155f2fSWarner Losh { 2168d2155f2fSWarner Losh 2169d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 2170d2155f2fSWarner Losh if (sc->sis_stopped) { 2171d2155f2fSWarner Losh SIS_UNLOCK(sc); 2172d2155f2fSWarner Losh return; 2173d2155f2fSWarner Losh } 2174d2155f2fSWarner Losh 2175d2155f2fSWarner Losh if (sc->sis_watchdog_timer == 0 || --sc->sis_watchdog_timer >0) 2176d2155f2fSWarner Losh return; 2177d2155f2fSWarner Losh 2178d2155f2fSWarner Losh device_printf(sc->sis_dev, "watchdog timeout\n"); 2179d2155f2fSWarner Losh sc->sis_ifp->if_oerrors++; 2180d2155f2fSWarner Losh 2181d2155f2fSWarner Losh sis_stop(sc); 2182d2155f2fSWarner Losh sis_reset(sc); 2183d2155f2fSWarner Losh sis_initl(sc); 2184d2155f2fSWarner Losh 2185d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&sc->sis_ifp->if_snd)) 2186d2155f2fSWarner Losh sis_startl(sc->sis_ifp); 2187d2155f2fSWarner Losh } 2188d2155f2fSWarner Losh 2189d2155f2fSWarner Losh /* 2190d2155f2fSWarner Losh * Stop the adapter and free any mbufs allocated to the 2191d2155f2fSWarner Losh * RX and TX lists. 2192d2155f2fSWarner Losh */ 2193d2155f2fSWarner Losh static void 2194d2155f2fSWarner Losh sis_stop(struct sis_softc *sc) 2195d2155f2fSWarner Losh { 2196d2155f2fSWarner Losh int i; 2197d2155f2fSWarner Losh struct ifnet *ifp; 2198d2155f2fSWarner Losh struct sis_desc *dp; 2199d2155f2fSWarner Losh 2200d2155f2fSWarner Losh if (sc->sis_stopped) 2201d2155f2fSWarner Losh return; 2202d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 2203d2155f2fSWarner Losh ifp = sc->sis_ifp; 2204d2155f2fSWarner Losh sc->sis_watchdog_timer = 0; 2205d2155f2fSWarner Losh 2206d2155f2fSWarner Losh callout_stop(&sc->sis_stat_ch); 2207d2155f2fSWarner Losh 2208d2155f2fSWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2209d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 2210d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IMR, 0); 2211d2155f2fSWarner Losh CSR_READ_4(sc, SIS_ISR); /* clear any interrupts already pending */ 2212d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 2213d2155f2fSWarner Losh DELAY(1000); 2214d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); 2215d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 2216d2155f2fSWarner Losh 2217d2155f2fSWarner Losh sc->sis_link = 0; 2218d2155f2fSWarner Losh 2219d2155f2fSWarner Losh /* 2220d2155f2fSWarner Losh * Free data in the RX lists. 2221d2155f2fSWarner Losh */ 2222d2155f2fSWarner Losh dp = &sc->sis_rx_list[0]; 2223d2155f2fSWarner Losh for (i = 0; i < SIS_RX_LIST_CNT; i++, dp++) { 2224d2155f2fSWarner Losh if (dp->sis_mbuf == NULL) 2225d2155f2fSWarner Losh continue; 2226d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_tag, dp->sis_map); 2227d2155f2fSWarner Losh bus_dmamap_destroy(sc->sis_tag, dp->sis_map); 2228d2155f2fSWarner Losh m_freem(dp->sis_mbuf); 2229d2155f2fSWarner Losh dp->sis_mbuf = NULL; 2230d2155f2fSWarner Losh } 2231d2155f2fSWarner Losh bzero(sc->sis_rx_list, SIS_RX_LIST_SZ); 2232d2155f2fSWarner Losh 2233d2155f2fSWarner Losh /* 2234d2155f2fSWarner Losh * Free the TX list buffers. 2235d2155f2fSWarner Losh */ 2236d2155f2fSWarner Losh dp = &sc->sis_tx_list[0]; 2237d2155f2fSWarner Losh for (i = 0; i < SIS_TX_LIST_CNT; i++, dp++) { 2238d2155f2fSWarner Losh if (dp->sis_mbuf == NULL) 2239d2155f2fSWarner Losh continue; 2240d2155f2fSWarner Losh bus_dmamap_unload(sc->sis_tag, dp->sis_map); 2241d2155f2fSWarner Losh bus_dmamap_destroy(sc->sis_tag, dp->sis_map); 2242d2155f2fSWarner Losh m_freem(dp->sis_mbuf); 2243d2155f2fSWarner Losh dp->sis_mbuf = NULL; 2244d2155f2fSWarner Losh } 2245d2155f2fSWarner Losh 2246d2155f2fSWarner Losh bzero(sc->sis_tx_list, SIS_TX_LIST_SZ); 2247d2155f2fSWarner Losh 2248d2155f2fSWarner Losh sc->sis_stopped = 1; 2249d2155f2fSWarner Losh } 2250d2155f2fSWarner Losh 2251d2155f2fSWarner Losh /* 2252d2155f2fSWarner Losh * Stop all chip I/O so that the kernel's probe routines don't 2253d2155f2fSWarner Losh * get confused by errant DMAs when rebooting. 2254d2155f2fSWarner Losh */ 2255d2155f2fSWarner Losh static void 2256d2155f2fSWarner Losh sis_shutdown(device_t dev) 2257d2155f2fSWarner Losh { 2258d2155f2fSWarner Losh struct sis_softc *sc; 2259d2155f2fSWarner Losh 2260d2155f2fSWarner Losh sc = device_get_softc(dev); 2261d2155f2fSWarner Losh SIS_LOCK(sc); 2262d2155f2fSWarner Losh sis_reset(sc); 2263d2155f2fSWarner Losh sis_stop(sc); 2264d2155f2fSWarner Losh SIS_UNLOCK(sc); 2265d2155f2fSWarner Losh } 2266d2155f2fSWarner Losh 2267d2155f2fSWarner Losh static device_method_t sis_methods[] = { 2268d2155f2fSWarner Losh /* Device interface */ 2269d2155f2fSWarner Losh DEVMETHOD(device_probe, sis_probe), 2270d2155f2fSWarner Losh DEVMETHOD(device_attach, sis_attach), 2271d2155f2fSWarner Losh DEVMETHOD(device_detach, sis_detach), 2272d2155f2fSWarner Losh DEVMETHOD(device_shutdown, sis_shutdown), 2273d2155f2fSWarner Losh 2274d2155f2fSWarner Losh /* bus interface */ 2275d2155f2fSWarner Losh DEVMETHOD(bus_print_child, bus_generic_print_child), 2276d2155f2fSWarner Losh DEVMETHOD(bus_driver_added, bus_generic_driver_added), 2277d2155f2fSWarner Losh 2278d2155f2fSWarner Losh /* MII interface */ 2279d2155f2fSWarner Losh DEVMETHOD(miibus_readreg, sis_miibus_readreg), 2280d2155f2fSWarner Losh DEVMETHOD(miibus_writereg, sis_miibus_writereg), 2281d2155f2fSWarner Losh DEVMETHOD(miibus_statchg, sis_miibus_statchg), 2282d2155f2fSWarner Losh 2283d2155f2fSWarner Losh { 0, 0 } 2284d2155f2fSWarner Losh }; 2285d2155f2fSWarner Losh 2286d2155f2fSWarner Losh static driver_t sis_driver = { 2287d2155f2fSWarner Losh "sis", 2288d2155f2fSWarner Losh sis_methods, 2289d2155f2fSWarner Losh sizeof(struct sis_softc) 2290d2155f2fSWarner Losh }; 2291d2155f2fSWarner Losh 2292d2155f2fSWarner Losh static devclass_t sis_devclass; 2293d2155f2fSWarner Losh 2294d2155f2fSWarner Losh DRIVER_MODULE(sis, pci, sis_driver, sis_devclass, 0, 0); 2295d2155f2fSWarner Losh DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); 2296