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> 67a629f2b1SPyun YongHyeon #include <sys/bus.h> 68a629f2b1SPyun YongHyeon #include <sys/endian.h> 69d2155f2fSWarner Losh #include <sys/kernel.h> 70a629f2b1SPyun YongHyeon #include <sys/lock.h> 71a629f2b1SPyun YongHyeon #include <sys/malloc.h> 72a629f2b1SPyun YongHyeon #include <sys/mbuf.h> 73d2155f2fSWarner Losh #include <sys/module.h> 74d2155f2fSWarner Losh #include <sys/socket.h> 75a629f2b1SPyun YongHyeon #include <sys/sockio.h> 7694222398SPyun YongHyeon #include <sys/sysctl.h> 77d2155f2fSWarner Losh 78d2155f2fSWarner Losh #include <net/if.h> 79d2155f2fSWarner Losh #include <net/if_arp.h> 80d2155f2fSWarner Losh #include <net/ethernet.h> 81d2155f2fSWarner Losh #include <net/if_dl.h> 82d2155f2fSWarner Losh #include <net/if_media.h> 83d2155f2fSWarner Losh #include <net/if_types.h> 84d2155f2fSWarner Losh #include <net/if_vlan_var.h> 85d2155f2fSWarner Losh 86d2155f2fSWarner Losh #include <net/bpf.h> 87d2155f2fSWarner Losh 88d2155f2fSWarner Losh #include <machine/bus.h> 89d2155f2fSWarner Losh #include <machine/resource.h> 90d2155f2fSWarner Losh #include <sys/bus.h> 91d2155f2fSWarner Losh #include <sys/rman.h> 92d2155f2fSWarner Losh 93d2155f2fSWarner Losh #include <dev/mii/mii.h> 94d2155f2fSWarner Losh #include <dev/mii/miivar.h> 95d2155f2fSWarner Losh 96d2155f2fSWarner Losh #include <dev/pci/pcireg.h> 97d2155f2fSWarner Losh #include <dev/pci/pcivar.h> 98d2155f2fSWarner Losh 99d2155f2fSWarner Losh #define SIS_USEIOSPACE 100d2155f2fSWarner Losh 101d2155f2fSWarner Losh #include <dev/sis/if_sisreg.h> 102d2155f2fSWarner Losh 103d2155f2fSWarner Losh MODULE_DEPEND(sis, pci, 1, 1, 1); 104d2155f2fSWarner Losh MODULE_DEPEND(sis, ether, 1, 1, 1); 105d2155f2fSWarner Losh MODULE_DEPEND(sis, miibus, 1, 1, 1); 106d2155f2fSWarner Losh 107d2155f2fSWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 108d2155f2fSWarner Losh #include "miibus_if.h" 109d2155f2fSWarner Losh 110d2155f2fSWarner Losh #define SIS_LOCK(_sc) mtx_lock(&(_sc)->sis_mtx) 111d2155f2fSWarner Losh #define SIS_UNLOCK(_sc) mtx_unlock(&(_sc)->sis_mtx) 112d2155f2fSWarner Losh #define SIS_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sis_mtx, MA_OWNED) 113d2155f2fSWarner Losh 114d2155f2fSWarner Losh /* 115d2155f2fSWarner Losh * register space access macros 116d2155f2fSWarner Losh */ 117d2155f2fSWarner Losh #define CSR_WRITE_4(sc, reg, val) bus_write_4(sc->sis_res[0], reg, val) 118d2155f2fSWarner Losh 119d2155f2fSWarner Losh #define CSR_READ_4(sc, reg) bus_read_4(sc->sis_res[0], reg) 120d2155f2fSWarner Losh 121d2155f2fSWarner Losh #define CSR_READ_2(sc, reg) bus_read_2(sc->sis_res[0], reg) 122d2155f2fSWarner Losh 123d2155f2fSWarner Losh /* 124d2155f2fSWarner Losh * Various supported device vendors/types and their names. 125d2155f2fSWarner Losh */ 126d2155f2fSWarner Losh static struct sis_type sis_devs[] = { 127d2155f2fSWarner Losh { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, 128d2155f2fSWarner Losh { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, 129d2155f2fSWarner Losh { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP8381[56] 10/100BaseTX" }, 130d2155f2fSWarner Losh { 0, 0, NULL } 131d2155f2fSWarner Losh }; 132d2155f2fSWarner Losh 133d2155f2fSWarner Losh static int sis_detach(device_t); 134a629f2b1SPyun YongHyeon static __inline void sis_discard_rxbuf(struct sis_rxdesc *); 135a629f2b1SPyun YongHyeon static int sis_dma_alloc(struct sis_softc *); 136a629f2b1SPyun YongHyeon static void sis_dma_free(struct sis_softc *); 137a629f2b1SPyun YongHyeon static int sis_dma_ring_alloc(struct sis_softc *, bus_size_t, bus_size_t, 138a629f2b1SPyun YongHyeon bus_dma_tag_t *, uint8_t **, bus_dmamap_t *, bus_addr_t *, const char *); 139a629f2b1SPyun YongHyeon static void sis_dmamap_cb(void *, bus_dma_segment_t *, int, int); 140a629f2b1SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 141a629f2b1SPyun YongHyeon static __inline void sis_fixup_rx(struct mbuf *); 142a629f2b1SPyun YongHyeon #endif 143d2155f2fSWarner Losh static void sis_ifmedia_sts(struct ifnet *, struct ifmediareq *); 144d2155f2fSWarner Losh static int sis_ifmedia_upd(struct ifnet *); 145d2155f2fSWarner Losh static void sis_init(void *); 146d2155f2fSWarner Losh static void sis_initl(struct sis_softc *); 147d2155f2fSWarner Losh static void sis_intr(void *); 148d2155f2fSWarner Losh static int sis_ioctl(struct ifnet *, u_long, caddr_t); 149a629f2b1SPyun YongHyeon static int sis_newbuf(struct sis_softc *, struct sis_rxdesc *); 150*0af3989bSPyun YongHyeon static int sis_resume(device_t); 151a629f2b1SPyun YongHyeon static int sis_rxeof(struct sis_softc *); 152d2155f2fSWarner Losh static void sis_start(struct ifnet *); 153d2155f2fSWarner Losh static void sis_startl(struct ifnet *); 154d2155f2fSWarner Losh static void sis_stop(struct sis_softc *); 155*0af3989bSPyun YongHyeon static int sis_suspend(device_t); 15694222398SPyun YongHyeon static void sis_add_sysctls(struct sis_softc *); 157d2155f2fSWarner Losh static void sis_watchdog(struct sis_softc *); 158*0af3989bSPyun YongHyeon static void sis_wol(struct sis_softc *); 159d2155f2fSWarner Losh 160d2155f2fSWarner Losh 161d2155f2fSWarner Losh static struct resource_spec sis_res_spec[] = { 162d2155f2fSWarner Losh #ifdef SIS_USEIOSPACE 163d2155f2fSWarner Losh { SYS_RES_IOPORT, SIS_PCI_LOIO, RF_ACTIVE}, 164d2155f2fSWarner Losh #else 165d2155f2fSWarner Losh { SYS_RES_MEMORY, SIS_PCI_LOMEM, RF_ACTIVE}, 166d2155f2fSWarner Losh #endif 167d2155f2fSWarner Losh { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 168d2155f2fSWarner Losh { -1, 0 } 169d2155f2fSWarner Losh }; 170d2155f2fSWarner Losh 171d2155f2fSWarner Losh #define SIS_SETBIT(sc, reg, x) \ 172d2155f2fSWarner Losh CSR_WRITE_4(sc, reg, \ 173d2155f2fSWarner Losh CSR_READ_4(sc, reg) | (x)) 174d2155f2fSWarner Losh 175d2155f2fSWarner Losh #define SIS_CLRBIT(sc, reg, x) \ 176d2155f2fSWarner Losh CSR_WRITE_4(sc, reg, \ 177d2155f2fSWarner Losh CSR_READ_4(sc, reg) & ~(x)) 178d2155f2fSWarner Losh 179d2155f2fSWarner Losh #define SIO_SET(x) \ 180d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) 181d2155f2fSWarner Losh 182d2155f2fSWarner Losh #define SIO_CLR(x) \ 183d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) 184d2155f2fSWarner Losh 185d2155f2fSWarner Losh /* 186d2155f2fSWarner Losh * Routine to reverse the bits in a word. Stolen almost 187d2155f2fSWarner Losh * verbatim from /usr/games/fortune. 188d2155f2fSWarner Losh */ 189d2155f2fSWarner Losh static uint16_t 190d2155f2fSWarner Losh sis_reverse(uint16_t n) 191d2155f2fSWarner Losh { 192d2155f2fSWarner Losh n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); 193d2155f2fSWarner Losh n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); 194d2155f2fSWarner Losh n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); 195d2155f2fSWarner Losh n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); 196d2155f2fSWarner Losh 197d2155f2fSWarner Losh return (n); 198d2155f2fSWarner Losh } 199d2155f2fSWarner Losh 200d2155f2fSWarner Losh static void 201d2155f2fSWarner Losh sis_delay(struct sis_softc *sc) 202d2155f2fSWarner Losh { 203d2155f2fSWarner Losh int idx; 204d2155f2fSWarner Losh 205d2155f2fSWarner Losh for (idx = (300 / 33) + 1; idx > 0; idx--) 206d2155f2fSWarner Losh CSR_READ_4(sc, SIS_CSR); 207d2155f2fSWarner Losh } 208d2155f2fSWarner Losh 209d2155f2fSWarner Losh static void 210d2155f2fSWarner Losh sis_eeprom_idle(struct sis_softc *sc) 211d2155f2fSWarner Losh { 212d2155f2fSWarner Losh int i; 213d2155f2fSWarner Losh 214d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CSEL); 215d2155f2fSWarner Losh sis_delay(sc); 216d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 217d2155f2fSWarner Losh sis_delay(sc); 218d2155f2fSWarner Losh 219d2155f2fSWarner Losh for (i = 0; i < 25; i++) { 220d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 221d2155f2fSWarner Losh sis_delay(sc); 222d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 223d2155f2fSWarner Losh sis_delay(sc); 224d2155f2fSWarner Losh } 225d2155f2fSWarner Losh 226d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 227d2155f2fSWarner Losh sis_delay(sc); 228d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CSEL); 229d2155f2fSWarner Losh sis_delay(sc); 230d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); 231d2155f2fSWarner Losh } 232d2155f2fSWarner Losh 233d2155f2fSWarner Losh /* 234d2155f2fSWarner Losh * Send a read command and address to the EEPROM, check for ACK. 235d2155f2fSWarner Losh */ 236d2155f2fSWarner Losh static void 237d2155f2fSWarner Losh sis_eeprom_putbyte(struct sis_softc *sc, int addr) 238d2155f2fSWarner Losh { 239d2155f2fSWarner Losh int d, i; 240d2155f2fSWarner Losh 241d2155f2fSWarner Losh d = addr | SIS_EECMD_READ; 242d2155f2fSWarner Losh 243d2155f2fSWarner Losh /* 244d2155f2fSWarner Losh * Feed in each bit and stobe the clock. 245d2155f2fSWarner Losh */ 246d2155f2fSWarner Losh for (i = 0x400; i; i >>= 1) { 247d2155f2fSWarner Losh if (d & i) { 248d2155f2fSWarner Losh SIO_SET(SIS_EECTL_DIN); 249d2155f2fSWarner Losh } else { 250d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_DIN); 251d2155f2fSWarner Losh } 252d2155f2fSWarner Losh sis_delay(sc); 253d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 254d2155f2fSWarner Losh sis_delay(sc); 255d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 256d2155f2fSWarner Losh sis_delay(sc); 257d2155f2fSWarner Losh } 258d2155f2fSWarner Losh } 259d2155f2fSWarner Losh 260d2155f2fSWarner Losh /* 261d2155f2fSWarner Losh * Read a word of data stored in the EEPROM at address 'addr.' 262d2155f2fSWarner Losh */ 263d2155f2fSWarner Losh static void 264d2155f2fSWarner Losh sis_eeprom_getword(struct sis_softc *sc, int addr, uint16_t *dest) 265d2155f2fSWarner Losh { 266d2155f2fSWarner Losh int i; 26791c265b8SPyun YongHyeon uint16_t word = 0; 268d2155f2fSWarner Losh 269d2155f2fSWarner Losh /* Force EEPROM to idle state. */ 270d2155f2fSWarner Losh sis_eeprom_idle(sc); 271d2155f2fSWarner Losh 272d2155f2fSWarner Losh /* Enter EEPROM access mode. */ 273d2155f2fSWarner Losh sis_delay(sc); 274d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 275d2155f2fSWarner Losh sis_delay(sc); 276d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CSEL); 277d2155f2fSWarner Losh sis_delay(sc); 278d2155f2fSWarner Losh 279d2155f2fSWarner Losh /* 280d2155f2fSWarner Losh * Send address of word we want to read. 281d2155f2fSWarner Losh */ 282d2155f2fSWarner Losh sis_eeprom_putbyte(sc, addr); 283d2155f2fSWarner Losh 284d2155f2fSWarner Losh /* 285d2155f2fSWarner Losh * Start reading bits from EEPROM. 286d2155f2fSWarner Losh */ 287d2155f2fSWarner Losh for (i = 0x8000; i; i >>= 1) { 288d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 289d2155f2fSWarner Losh sis_delay(sc); 290d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) 291d2155f2fSWarner Losh word |= i; 292d2155f2fSWarner Losh sis_delay(sc); 293d2155f2fSWarner Losh SIO_CLR(SIS_EECTL_CLK); 294d2155f2fSWarner Losh sis_delay(sc); 295d2155f2fSWarner Losh } 296d2155f2fSWarner Losh 297d2155f2fSWarner Losh /* Turn off EEPROM access mode. */ 298d2155f2fSWarner Losh sis_eeprom_idle(sc); 299d2155f2fSWarner Losh 300d2155f2fSWarner Losh *dest = word; 301d2155f2fSWarner Losh } 302d2155f2fSWarner Losh 303d2155f2fSWarner Losh /* 304d2155f2fSWarner Losh * Read a sequence of words from the EEPROM. 305d2155f2fSWarner Losh */ 306d2155f2fSWarner Losh static void 307d2155f2fSWarner Losh sis_read_eeprom(struct sis_softc *sc, caddr_t dest, int off, int cnt, int swap) 308d2155f2fSWarner Losh { 309d2155f2fSWarner Losh int i; 31091c265b8SPyun YongHyeon uint16_t word = 0, *ptr; 311d2155f2fSWarner Losh 312d2155f2fSWarner Losh for (i = 0; i < cnt; i++) { 313d2155f2fSWarner Losh sis_eeprom_getword(sc, off + i, &word); 31491c265b8SPyun YongHyeon ptr = (uint16_t *)(dest + (i * 2)); 315d2155f2fSWarner Losh if (swap) 316d2155f2fSWarner Losh *ptr = ntohs(word); 317d2155f2fSWarner Losh else 318d2155f2fSWarner Losh *ptr = word; 319d2155f2fSWarner Losh } 320d2155f2fSWarner Losh } 321d2155f2fSWarner Losh 322d2155f2fSWarner Losh #if defined(__i386__) || defined(__amd64__) 323d2155f2fSWarner Losh static device_t 324d2155f2fSWarner Losh sis_find_bridge(device_t dev) 325d2155f2fSWarner Losh { 326d2155f2fSWarner Losh devclass_t pci_devclass; 327d2155f2fSWarner Losh device_t *pci_devices; 328d2155f2fSWarner Losh int pci_count = 0; 329d2155f2fSWarner Losh device_t *pci_children; 330d2155f2fSWarner Losh int pci_childcount = 0; 331d2155f2fSWarner Losh device_t *busp, *childp; 332d2155f2fSWarner Losh device_t child = NULL; 333d2155f2fSWarner Losh int i, j; 334d2155f2fSWarner Losh 335d2155f2fSWarner Losh if ((pci_devclass = devclass_find("pci")) == NULL) 336d2155f2fSWarner Losh return (NULL); 337d2155f2fSWarner Losh 338d2155f2fSWarner Losh devclass_get_devices(pci_devclass, &pci_devices, &pci_count); 339d2155f2fSWarner Losh 340d2155f2fSWarner Losh for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) { 34131063462SWarner Losh if (device_get_children(*busp, &pci_children, &pci_childcount)) 34231063462SWarner Losh continue; 343d2155f2fSWarner Losh for (j = 0, childp = pci_children; 344d2155f2fSWarner Losh j < pci_childcount; j++, childp++) { 345d2155f2fSWarner Losh if (pci_get_vendor(*childp) == SIS_VENDORID && 346d2155f2fSWarner Losh pci_get_device(*childp) == 0x0008) { 347d2155f2fSWarner Losh child = *childp; 34831063462SWarner Losh free(pci_children, M_TEMP); 349d2155f2fSWarner Losh goto done; 350d2155f2fSWarner Losh } 351d2155f2fSWarner Losh } 35231063462SWarner Losh free(pci_children, M_TEMP); 353d2155f2fSWarner Losh } 354d2155f2fSWarner Losh 355d2155f2fSWarner Losh done: 356d2155f2fSWarner Losh free(pci_devices, M_TEMP); 357d2155f2fSWarner Losh return (child); 358d2155f2fSWarner Losh } 359d2155f2fSWarner Losh 360d2155f2fSWarner Losh static void 361d2155f2fSWarner Losh sis_read_cmos(struct sis_softc *sc, device_t dev, caddr_t dest, int off, int cnt) 362d2155f2fSWarner Losh { 363d2155f2fSWarner Losh device_t bridge; 36491c265b8SPyun YongHyeon uint8_t reg; 365d2155f2fSWarner Losh int i; 366d2155f2fSWarner Losh bus_space_tag_t btag; 367d2155f2fSWarner Losh 368d2155f2fSWarner Losh bridge = sis_find_bridge(dev); 369d2155f2fSWarner Losh if (bridge == NULL) 370d2155f2fSWarner Losh return; 371d2155f2fSWarner Losh reg = pci_read_config(bridge, 0x48, 1); 372d2155f2fSWarner Losh pci_write_config(bridge, 0x48, reg|0x40, 1); 373d2155f2fSWarner Losh 374d2155f2fSWarner Losh /* XXX */ 375d2155f2fSWarner Losh #if defined(__i386__) 376d2155f2fSWarner Losh btag = I386_BUS_SPACE_IO; 377d2155f2fSWarner Losh #elif defined(__amd64__) 378d2155f2fSWarner Losh btag = AMD64_BUS_SPACE_IO; 379d2155f2fSWarner Losh #endif 380d2155f2fSWarner Losh 381d2155f2fSWarner Losh for (i = 0; i < cnt; i++) { 382d2155f2fSWarner Losh bus_space_write_1(btag, 0x0, 0x70, i + off); 383d2155f2fSWarner Losh *(dest + i) = bus_space_read_1(btag, 0x0, 0x71); 384d2155f2fSWarner Losh } 385d2155f2fSWarner Losh 386d2155f2fSWarner Losh pci_write_config(bridge, 0x48, reg & ~0x40, 1); 387d2155f2fSWarner Losh } 388d2155f2fSWarner Losh 389d2155f2fSWarner Losh static void 390d2155f2fSWarner Losh sis_read_mac(struct sis_softc *sc, device_t dev, caddr_t dest) 391d2155f2fSWarner Losh { 39291c265b8SPyun YongHyeon uint32_t filtsave, csrsave; 393d2155f2fSWarner Losh 394d2155f2fSWarner Losh filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 395d2155f2fSWarner Losh csrsave = CSR_READ_4(sc, SIS_CSR); 396d2155f2fSWarner Losh 397d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, SIS_CSR_RELOAD | filtsave); 398d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, 0); 399d2155f2fSWarner Losh 400d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave & ~SIS_RXFILTCTL_ENABLE); 401d2155f2fSWarner Losh 402d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 40391c265b8SPyun YongHyeon ((uint16_t *)dest)[0] = CSR_READ_2(sc, SIS_RXFILT_DATA); 404d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL,SIS_FILTADDR_PAR1); 40591c265b8SPyun YongHyeon ((uint16_t *)dest)[1] = CSR_READ_2(sc, SIS_RXFILT_DATA); 406d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 40791c265b8SPyun YongHyeon ((uint16_t *)dest)[2] = CSR_READ_2(sc, SIS_RXFILT_DATA); 408d2155f2fSWarner Losh 409d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 410d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_CSR, csrsave); 411d2155f2fSWarner Losh } 412d2155f2fSWarner Losh #endif 413d2155f2fSWarner Losh 414d2155f2fSWarner Losh /* 415d2155f2fSWarner Losh * Sync the PHYs by setting data bit and strobing the clock 32 times. 416d2155f2fSWarner Losh */ 417d2155f2fSWarner Losh static void 418d2155f2fSWarner Losh sis_mii_sync(struct sis_softc *sc) 419d2155f2fSWarner Losh { 420d2155f2fSWarner Losh int i; 421d2155f2fSWarner Losh 422d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR|SIS_MII_DATA); 423d2155f2fSWarner Losh 424d2155f2fSWarner Losh for (i = 0; i < 32; i++) { 425d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 426d2155f2fSWarner Losh DELAY(1); 427d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 428d2155f2fSWarner Losh DELAY(1); 429d2155f2fSWarner Losh } 430d2155f2fSWarner Losh } 431d2155f2fSWarner Losh 432d2155f2fSWarner Losh /* 433d2155f2fSWarner Losh * Clock a series of bits through the MII. 434d2155f2fSWarner Losh */ 435d2155f2fSWarner Losh static void 436d2155f2fSWarner Losh sis_mii_send(struct sis_softc *sc, uint32_t bits, int cnt) 437d2155f2fSWarner Losh { 438d2155f2fSWarner Losh int i; 439d2155f2fSWarner Losh 440d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 441d2155f2fSWarner Losh 442d2155f2fSWarner Losh for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 443d2155f2fSWarner Losh if (bits & i) { 444d2155f2fSWarner Losh SIO_SET(SIS_MII_DATA); 445d2155f2fSWarner Losh } else { 446d2155f2fSWarner Losh SIO_CLR(SIS_MII_DATA); 447d2155f2fSWarner Losh } 448d2155f2fSWarner Losh DELAY(1); 449d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 450d2155f2fSWarner Losh DELAY(1); 451d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 452d2155f2fSWarner Losh } 453d2155f2fSWarner Losh } 454d2155f2fSWarner Losh 455d2155f2fSWarner Losh /* 456d2155f2fSWarner Losh * Read an PHY register through the MII. 457d2155f2fSWarner Losh */ 458d2155f2fSWarner Losh static int 459d2155f2fSWarner Losh sis_mii_readreg(struct sis_softc *sc, struct sis_mii_frame *frame) 460d2155f2fSWarner Losh { 461d2155f2fSWarner Losh int i, ack; 462d2155f2fSWarner Losh 463d2155f2fSWarner Losh /* 464d2155f2fSWarner Losh * Set up frame for RX. 465d2155f2fSWarner Losh */ 466d2155f2fSWarner Losh frame->mii_stdelim = SIS_MII_STARTDELIM; 467d2155f2fSWarner Losh frame->mii_opcode = SIS_MII_READOP; 468d2155f2fSWarner Losh frame->mii_turnaround = 0; 469d2155f2fSWarner Losh frame->mii_data = 0; 470d2155f2fSWarner Losh 471d2155f2fSWarner Losh /* 472d2155f2fSWarner Losh * Turn on data xmit. 473d2155f2fSWarner Losh */ 474d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR); 475d2155f2fSWarner Losh 476d2155f2fSWarner Losh sis_mii_sync(sc); 477d2155f2fSWarner Losh 478d2155f2fSWarner Losh /* 479d2155f2fSWarner Losh * Send command/address info. 480d2155f2fSWarner Losh */ 481d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_stdelim, 2); 482d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_opcode, 2); 483d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_phyaddr, 5); 484d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_regaddr, 5); 485d2155f2fSWarner Losh 486d2155f2fSWarner Losh /* Idle bit */ 487d2155f2fSWarner Losh SIO_CLR((SIS_MII_CLK|SIS_MII_DATA)); 488d2155f2fSWarner Losh DELAY(1); 489d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 490d2155f2fSWarner Losh DELAY(1); 491d2155f2fSWarner Losh 492d2155f2fSWarner Losh /* Turn off xmit. */ 493d2155f2fSWarner Losh SIO_CLR(SIS_MII_DIR); 494d2155f2fSWarner Losh 495d2155f2fSWarner Losh /* Check for ack */ 496d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 497d2155f2fSWarner Losh DELAY(1); 498d2155f2fSWarner Losh ack = CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA; 499d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 500d2155f2fSWarner Losh DELAY(1); 501d2155f2fSWarner Losh 502d2155f2fSWarner Losh /* 503d2155f2fSWarner Losh * Now try reading data bits. If the ack failed, we still 504d2155f2fSWarner Losh * need to clock through 16 cycles to keep the PHY(s) in sync. 505d2155f2fSWarner Losh */ 506d2155f2fSWarner Losh if (ack) { 507d2155f2fSWarner Losh for (i = 0; i < 16; i++) { 508d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 509d2155f2fSWarner Losh DELAY(1); 510d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 511d2155f2fSWarner Losh DELAY(1); 512d2155f2fSWarner Losh } 513d2155f2fSWarner Losh goto fail; 514d2155f2fSWarner Losh } 515d2155f2fSWarner Losh 516d2155f2fSWarner Losh for (i = 0x8000; i; i >>= 1) { 517d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 518d2155f2fSWarner Losh DELAY(1); 519d2155f2fSWarner Losh if (!ack) { 520d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA) 521d2155f2fSWarner Losh frame->mii_data |= i; 522d2155f2fSWarner Losh DELAY(1); 523d2155f2fSWarner Losh } 524d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 525d2155f2fSWarner Losh DELAY(1); 526d2155f2fSWarner Losh } 527d2155f2fSWarner Losh 528d2155f2fSWarner Losh fail: 529d2155f2fSWarner Losh 530d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 531d2155f2fSWarner Losh DELAY(1); 532d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 533d2155f2fSWarner Losh DELAY(1); 534d2155f2fSWarner Losh 535d2155f2fSWarner Losh if (ack) 536d2155f2fSWarner Losh return (1); 537d2155f2fSWarner Losh return (0); 538d2155f2fSWarner Losh } 539d2155f2fSWarner Losh 540d2155f2fSWarner Losh /* 541d2155f2fSWarner Losh * Write to a PHY register through the MII. 542d2155f2fSWarner Losh */ 543d2155f2fSWarner Losh static int 544d2155f2fSWarner Losh sis_mii_writereg(struct sis_softc *sc, struct sis_mii_frame *frame) 545d2155f2fSWarner Losh { 546d2155f2fSWarner Losh 547d2155f2fSWarner Losh /* 548d2155f2fSWarner Losh * Set up frame for TX. 549d2155f2fSWarner Losh */ 550d2155f2fSWarner Losh 551d2155f2fSWarner Losh frame->mii_stdelim = SIS_MII_STARTDELIM; 552d2155f2fSWarner Losh frame->mii_opcode = SIS_MII_WRITEOP; 553d2155f2fSWarner Losh frame->mii_turnaround = SIS_MII_TURNAROUND; 554d2155f2fSWarner Losh 555d2155f2fSWarner Losh /* 556d2155f2fSWarner Losh * Turn on data output. 557d2155f2fSWarner Losh */ 558d2155f2fSWarner Losh SIO_SET(SIS_MII_DIR); 559d2155f2fSWarner Losh 560d2155f2fSWarner Losh sis_mii_sync(sc); 561d2155f2fSWarner Losh 562d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_stdelim, 2); 563d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_opcode, 2); 564d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_phyaddr, 5); 565d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_regaddr, 5); 566d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_turnaround, 2); 567d2155f2fSWarner Losh sis_mii_send(sc, frame->mii_data, 16); 568d2155f2fSWarner Losh 569d2155f2fSWarner Losh /* Idle bit. */ 570d2155f2fSWarner Losh SIO_SET(SIS_MII_CLK); 571d2155f2fSWarner Losh DELAY(1); 572d2155f2fSWarner Losh SIO_CLR(SIS_MII_CLK); 573d2155f2fSWarner Losh DELAY(1); 574d2155f2fSWarner Losh 575d2155f2fSWarner Losh /* 576d2155f2fSWarner Losh * Turn off xmit. 577d2155f2fSWarner Losh */ 578d2155f2fSWarner Losh SIO_CLR(SIS_MII_DIR); 579d2155f2fSWarner Losh 580d2155f2fSWarner Losh return (0); 581d2155f2fSWarner Losh } 582d2155f2fSWarner Losh 583d2155f2fSWarner Losh static int 584d2155f2fSWarner Losh sis_miibus_readreg(device_t dev, int phy, int reg) 585d2155f2fSWarner Losh { 586d2155f2fSWarner Losh struct sis_softc *sc; 587d2155f2fSWarner Losh struct sis_mii_frame frame; 588d2155f2fSWarner Losh 589d2155f2fSWarner Losh sc = device_get_softc(dev); 590d2155f2fSWarner Losh 591d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 592d2155f2fSWarner Losh if (phy != 0) 593d2155f2fSWarner Losh return (0); 594d2155f2fSWarner Losh /* 595d2155f2fSWarner Losh * The NatSemi chip can take a while after 596d2155f2fSWarner Losh * a reset to come ready, during which the BMSR 597d2155f2fSWarner Losh * returns a value of 0. This is *never* supposed 598d2155f2fSWarner Losh * to happen: some of the BMSR bits are meant to 599d2155f2fSWarner Losh * be hardwired in the on position, and this can 600d2155f2fSWarner Losh * confuse the miibus code a bit during the probe 601d2155f2fSWarner Losh * and attach phase. So we make an effort to check 602d2155f2fSWarner Losh * for this condition and wait for it to clear. 603d2155f2fSWarner Losh */ 604d2155f2fSWarner Losh if (!CSR_READ_4(sc, NS_BMSR)) 605d2155f2fSWarner Losh DELAY(1000); 606d2155f2fSWarner Losh return CSR_READ_4(sc, NS_BMCR + (reg * 4)); 607d2155f2fSWarner Losh } 608d2155f2fSWarner Losh 609d2155f2fSWarner Losh /* 610d2155f2fSWarner Losh * Chipsets < SIS_635 seem not to be able to read/write 611d2155f2fSWarner Losh * through mdio. Use the enhanced PHY access register 612d2155f2fSWarner Losh * again for them. 613d2155f2fSWarner Losh */ 614d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 615d2155f2fSWarner Losh sc->sis_rev < SIS_REV_635) { 616d2155f2fSWarner Losh int i, val = 0; 617d2155f2fSWarner Losh 618d2155f2fSWarner Losh if (phy != 0) 619d2155f2fSWarner Losh return (0); 620d2155f2fSWarner Losh 621d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_PHYCTL, 622d2155f2fSWarner Losh (phy << 11) | (reg << 6) | SIS_PHYOP_READ); 623d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 624d2155f2fSWarner Losh 625d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 626d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 627d2155f2fSWarner Losh break; 628d2155f2fSWarner Losh } 629d2155f2fSWarner Losh 630d2155f2fSWarner Losh if (i == SIS_TIMEOUT) { 631d2155f2fSWarner Losh device_printf(sc->sis_dev, "PHY failed to come ready\n"); 632d2155f2fSWarner Losh return (0); 633d2155f2fSWarner Losh } 634d2155f2fSWarner Losh 635d2155f2fSWarner Losh val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; 636d2155f2fSWarner Losh 637d2155f2fSWarner Losh if (val == 0xFFFF) 638d2155f2fSWarner Losh return (0); 639d2155f2fSWarner Losh 640d2155f2fSWarner Losh return (val); 641d2155f2fSWarner Losh } else { 642d2155f2fSWarner Losh bzero((char *)&frame, sizeof(frame)); 643d2155f2fSWarner Losh 644d2155f2fSWarner Losh frame.mii_phyaddr = phy; 645d2155f2fSWarner Losh frame.mii_regaddr = reg; 646d2155f2fSWarner Losh sis_mii_readreg(sc, &frame); 647d2155f2fSWarner Losh 648d2155f2fSWarner Losh return (frame.mii_data); 649d2155f2fSWarner Losh } 650d2155f2fSWarner Losh } 651d2155f2fSWarner Losh 652d2155f2fSWarner Losh static int 653d2155f2fSWarner Losh sis_miibus_writereg(device_t dev, int phy, int reg, int data) 654d2155f2fSWarner Losh { 655d2155f2fSWarner Losh struct sis_softc *sc; 656d2155f2fSWarner Losh struct sis_mii_frame frame; 657d2155f2fSWarner Losh 658d2155f2fSWarner Losh sc = device_get_softc(dev); 659d2155f2fSWarner Losh 660d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 661d2155f2fSWarner Losh if (phy != 0) 662d2155f2fSWarner Losh return (0); 663d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); 664d2155f2fSWarner Losh return (0); 665d2155f2fSWarner Losh } 666d2155f2fSWarner Losh 667d2155f2fSWarner Losh /* 668d2155f2fSWarner Losh * Chipsets < SIS_635 seem not to be able to read/write 669d2155f2fSWarner Losh * through mdio. Use the enhanced PHY access register 670d2155f2fSWarner Losh * again for them. 671d2155f2fSWarner Losh */ 672d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 673d2155f2fSWarner Losh sc->sis_rev < SIS_REV_635) { 674d2155f2fSWarner Losh int i; 675d2155f2fSWarner Losh 676d2155f2fSWarner Losh if (phy != 0) 677d2155f2fSWarner Losh return (0); 678d2155f2fSWarner Losh 679d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | 680d2155f2fSWarner Losh (reg << 6) | SIS_PHYOP_WRITE); 681d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 682d2155f2fSWarner Losh 683d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 684d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 685d2155f2fSWarner Losh break; 686d2155f2fSWarner Losh } 687d2155f2fSWarner Losh 688d2155f2fSWarner Losh if (i == SIS_TIMEOUT) 689d2155f2fSWarner Losh device_printf(sc->sis_dev, "PHY failed to come ready\n"); 690d2155f2fSWarner Losh } else { 691d2155f2fSWarner Losh bzero((char *)&frame, sizeof(frame)); 692d2155f2fSWarner Losh 693d2155f2fSWarner Losh frame.mii_phyaddr = phy; 694d2155f2fSWarner Losh frame.mii_regaddr = reg; 695d2155f2fSWarner Losh frame.mii_data = data; 696d2155f2fSWarner Losh sis_mii_writereg(sc, &frame); 697d2155f2fSWarner Losh } 698d2155f2fSWarner Losh return (0); 699d2155f2fSWarner Losh } 700d2155f2fSWarner Losh 701d2155f2fSWarner Losh static void 702d2155f2fSWarner Losh sis_miibus_statchg(device_t dev) 703d2155f2fSWarner Losh { 704d2155f2fSWarner Losh struct sis_softc *sc; 705d7b57e79SPyun YongHyeon struct mii_data *mii; 706d7b57e79SPyun YongHyeon struct ifnet *ifp; 707d7b57e79SPyun YongHyeon uint32_t reg; 708d2155f2fSWarner Losh 709d2155f2fSWarner Losh sc = device_get_softc(dev); 710d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 711d7b57e79SPyun YongHyeon 712d7b57e79SPyun YongHyeon mii = device_get_softc(sc->sis_miibus); 713d7b57e79SPyun YongHyeon ifp = sc->sis_ifp; 714d7b57e79SPyun YongHyeon if (mii == NULL || ifp == NULL || 715d7b57e79SPyun YongHyeon (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 716d7b57e79SPyun YongHyeon return; 717d7b57e79SPyun YongHyeon 71894222398SPyun YongHyeon sc->sis_flags &= ~SIS_FLAG_LINK; 719d7b57e79SPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 720d7b57e79SPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) { 721d7b57e79SPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) { 722d7b57e79SPyun YongHyeon case IFM_10_T: 723d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10); 72494222398SPyun YongHyeon sc->sis_flags |= SIS_FLAG_LINK; 725d7b57e79SPyun YongHyeon break; 726d7b57e79SPyun YongHyeon case IFM_100_TX: 727d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); 72894222398SPyun YongHyeon sc->sis_flags |= SIS_FLAG_LINK; 729d7b57e79SPyun YongHyeon break; 730d7b57e79SPyun YongHyeon default: 731d7b57e79SPyun YongHyeon break; 732d7b57e79SPyun YongHyeon } 733d7b57e79SPyun YongHyeon } 734d7b57e79SPyun YongHyeon 73594222398SPyun YongHyeon if ((sc->sis_flags & SIS_FLAG_LINK) == 0) { 736d7b57e79SPyun YongHyeon /* 737d7b57e79SPyun YongHyeon * Stopping MACs seem to reset SIS_TX_LISTPTR and 738d7b57e79SPyun YongHyeon * SIS_RX_LISTPTR which in turn requires resetting 739d7b57e79SPyun YongHyeon * TX/RX buffers. So just don't do anything for 740d7b57e79SPyun YongHyeon * lost link. 741d7b57e79SPyun YongHyeon */ 742d7b57e79SPyun YongHyeon return; 743d7b57e79SPyun YongHyeon } 744d7b57e79SPyun YongHyeon 745d7b57e79SPyun YongHyeon /* Set full/half duplex mode. */ 746d7b57e79SPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 747d7b57e79SPyun YongHyeon SIS_SETBIT(sc, SIS_TX_CFG, 748d7b57e79SPyun YongHyeon (SIS_TXCFG_IGN_HBEAT | SIS_TXCFG_IGN_CARR)); 749d7b57e79SPyun YongHyeon SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 750d7b57e79SPyun YongHyeon } else { 751d7b57e79SPyun YongHyeon SIS_CLRBIT(sc, SIS_TX_CFG, 752d7b57e79SPyun YongHyeon (SIS_TXCFG_IGN_HBEAT | SIS_TXCFG_IGN_CARR)); 753d7b57e79SPyun YongHyeon SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 754d7b57e79SPyun YongHyeon } 755d7b57e79SPyun YongHyeon 756d7b57e79SPyun YongHyeon if (sc->sis_type == SIS_TYPE_83816) { 757d7b57e79SPyun YongHyeon /* 758d7b57e79SPyun YongHyeon * MPII03.D: Half Duplex Excessive Collisions. 759d7b57e79SPyun YongHyeon * Also page 49 in 83816 manual 760d7b57e79SPyun YongHyeon */ 761d7b57e79SPyun YongHyeon SIS_SETBIT(sc, SIS_TX_CFG, SIS_TXCFG_MPII03D); 762d7b57e79SPyun YongHyeon } 763d7b57e79SPyun YongHyeon 764d7b57e79SPyun YongHyeon if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr < NS_SRR_16A && 765d7b57e79SPyun YongHyeon IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 766d7b57e79SPyun YongHyeon /* 767d7b57e79SPyun YongHyeon * Short Cable Receive Errors (MP21.E) 768d7b57e79SPyun YongHyeon */ 769d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); 770d7b57e79SPyun YongHyeon reg = CSR_READ_4(sc, NS_PHY_DSPCFG) & 0xfff; 771d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, NS_PHY_DSPCFG, reg | 0x1000); 772d7b57e79SPyun YongHyeon DELAY(100); 773d7b57e79SPyun YongHyeon reg = CSR_READ_4(sc, NS_PHY_TDATA) & 0xff; 774d7b57e79SPyun YongHyeon if ((reg & 0x0080) == 0 || (reg > 0xd8 && reg <= 0xff)) { 775d7b57e79SPyun YongHyeon device_printf(sc->sis_dev, 776d7b57e79SPyun YongHyeon "Applying short cable fix (reg=%x)\n", reg); 777d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, NS_PHY_TDATA, 0x00e8); 778d7b57e79SPyun YongHyeon SIS_SETBIT(sc, NS_PHY_DSPCFG, 0x20); 779d7b57e79SPyun YongHyeon } 780d7b57e79SPyun YongHyeon CSR_WRITE_4(sc, NS_PHY_PAGE, 0); 781d7b57e79SPyun YongHyeon } 782d7b57e79SPyun YongHyeon /* Enable TX/RX MACs. */ 783d7b57e79SPyun YongHyeon SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE | SIS_CSR_RX_DISABLE); 784d7b57e79SPyun YongHyeon SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE | SIS_CSR_RX_ENABLE); 785d2155f2fSWarner Losh } 786d2155f2fSWarner Losh 787d2155f2fSWarner Losh static uint32_t 788d2155f2fSWarner Losh sis_mchash(struct sis_softc *sc, const uint8_t *addr) 789d2155f2fSWarner Losh { 790d2155f2fSWarner Losh uint32_t crc; 791d2155f2fSWarner Losh 792d2155f2fSWarner Losh /* Compute CRC for the address value. */ 793d2155f2fSWarner Losh crc = ether_crc32_be(addr, ETHER_ADDR_LEN); 794d2155f2fSWarner Losh 795d2155f2fSWarner Losh /* 796d2155f2fSWarner Losh * return the filter bit position 797d2155f2fSWarner Losh * 798d2155f2fSWarner Losh * The NatSemi chip has a 512-bit filter, which is 799d2155f2fSWarner Losh * different than the SiS, so we special-case it. 800d2155f2fSWarner Losh */ 801d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 802d2155f2fSWarner Losh return (crc >> 23); 803d2155f2fSWarner Losh else if (sc->sis_rev >= SIS_REV_635 || 804d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B) 805d2155f2fSWarner Losh return (crc >> 24); 806d2155f2fSWarner Losh else 807d2155f2fSWarner Losh return (crc >> 25); 808d2155f2fSWarner Losh } 809d2155f2fSWarner Losh 810d2155f2fSWarner Losh static void 811d2155f2fSWarner Losh sis_setmulti_ns(struct sis_softc *sc) 812d2155f2fSWarner Losh { 813d2155f2fSWarner Losh struct ifnet *ifp; 814d2155f2fSWarner Losh struct ifmultiaddr *ifma; 81591c265b8SPyun YongHyeon uint32_t h = 0, i, filtsave; 816d2155f2fSWarner Losh int bit, index; 817d2155f2fSWarner Losh 818d2155f2fSWarner Losh ifp = sc->sis_ifp; 819d2155f2fSWarner Losh 820d2155f2fSWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 821d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 822d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 823d2155f2fSWarner Losh return; 824d2155f2fSWarner Losh } 825d2155f2fSWarner Losh 826d2155f2fSWarner Losh /* 827d2155f2fSWarner Losh * We have to explicitly enable the multicast hash table 828d2155f2fSWarner Losh * on the NatSemi chip if we want to use it, which we do. 829d2155f2fSWarner Losh */ 830d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 831d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 832d2155f2fSWarner Losh 833d2155f2fSWarner Losh filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 834d2155f2fSWarner Losh 835d2155f2fSWarner Losh /* first, zot all the existing hash bits */ 836d2155f2fSWarner Losh for (i = 0; i < 32; i++) { 837d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); 838d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 839d2155f2fSWarner Losh } 840d2155f2fSWarner Losh 841eb956cd0SRobert Watson if_maddr_rlock(ifp); 842d2155f2fSWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 843d2155f2fSWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 844d2155f2fSWarner Losh continue; 845d2155f2fSWarner Losh h = sis_mchash(sc, 846d2155f2fSWarner Losh LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 847d2155f2fSWarner Losh index = h >> 3; 848d2155f2fSWarner Losh bit = h & 0x1F; 849d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); 850d2155f2fSWarner Losh if (bit > 0xF) 851d2155f2fSWarner Losh bit -= 0x10; 852d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); 853d2155f2fSWarner Losh } 854eb956cd0SRobert Watson if_maddr_runlock(ifp); 855d2155f2fSWarner Losh 856d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 857d2155f2fSWarner Losh } 858d2155f2fSWarner Losh 859d2155f2fSWarner Losh static void 860d2155f2fSWarner Losh sis_setmulti_sis(struct sis_softc *sc) 861d2155f2fSWarner Losh { 862d2155f2fSWarner Losh struct ifnet *ifp; 863d2155f2fSWarner Losh struct ifmultiaddr *ifma; 86491c265b8SPyun YongHyeon uint32_t h, i, n, ctl; 86591c265b8SPyun YongHyeon uint16_t hashes[16]; 866d2155f2fSWarner Losh 867d2155f2fSWarner Losh ifp = sc->sis_ifp; 868d2155f2fSWarner Losh 869d2155f2fSWarner Losh /* hash table size */ 870d2155f2fSWarner Losh if (sc->sis_rev >= SIS_REV_635 || 871d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B) 872d2155f2fSWarner Losh n = 16; 873d2155f2fSWarner Losh else 874d2155f2fSWarner Losh n = 8; 875d2155f2fSWarner Losh 876d2155f2fSWarner Losh ctl = CSR_READ_4(sc, SIS_RXFILT_CTL) & SIS_RXFILTCTL_ENABLE; 877d2155f2fSWarner Losh 878d2155f2fSWarner Losh if (ifp->if_flags & IFF_BROADCAST) 879d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_BROAD; 880d2155f2fSWarner Losh 881d2155f2fSWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 882d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_ALLMULTI; 883d2155f2fSWarner Losh if (ifp->if_flags & IFF_PROMISC) 884d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_BROAD|SIS_RXFILTCTL_ALLPHYS; 885d2155f2fSWarner Losh for (i = 0; i < n; i++) 886d2155f2fSWarner Losh hashes[i] = ~0; 887d2155f2fSWarner Losh } else { 888d2155f2fSWarner Losh for (i = 0; i < n; i++) 889d2155f2fSWarner Losh hashes[i] = 0; 890d2155f2fSWarner Losh i = 0; 891eb956cd0SRobert Watson if_maddr_rlock(ifp); 892d2155f2fSWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 893d2155f2fSWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 894d2155f2fSWarner Losh continue; 895d2155f2fSWarner Losh h = sis_mchash(sc, 896d2155f2fSWarner Losh LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 897d2155f2fSWarner Losh hashes[h >> 4] |= 1 << (h & 0xf); 898d2155f2fSWarner Losh i++; 899d2155f2fSWarner Losh } 900eb956cd0SRobert Watson if_maddr_runlock(ifp); 901d2155f2fSWarner Losh if (i > n) { 902d2155f2fSWarner Losh ctl |= SIS_RXFILTCTL_ALLMULTI; 903d2155f2fSWarner Losh for (i = 0; i < n; i++) 904d2155f2fSWarner Losh hashes[i] = ~0; 905d2155f2fSWarner Losh } 906d2155f2fSWarner Losh } 907d2155f2fSWarner Losh 908d2155f2fSWarner Losh for (i = 0; i < n; i++) { 909d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + i) << 16); 910d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_DATA, hashes[i]); 911d2155f2fSWarner Losh } 912d2155f2fSWarner Losh 913d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, ctl); 914d2155f2fSWarner Losh } 915d2155f2fSWarner Losh 916d2155f2fSWarner Losh static void 917d2155f2fSWarner Losh sis_reset(struct sis_softc *sc) 918d2155f2fSWarner Losh { 919d2155f2fSWarner Losh int i; 920d2155f2fSWarner Losh 921d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); 922d2155f2fSWarner Losh 923d2155f2fSWarner Losh for (i = 0; i < SIS_TIMEOUT; i++) { 924d2155f2fSWarner Losh if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) 925d2155f2fSWarner Losh break; 926d2155f2fSWarner Losh } 927d2155f2fSWarner Losh 928d2155f2fSWarner Losh if (i == SIS_TIMEOUT) 929d2155f2fSWarner Losh device_printf(sc->sis_dev, "reset never completed\n"); 930d2155f2fSWarner Losh 931d2155f2fSWarner Losh /* Wait a little while for the chip to get its brains in order. */ 932d2155f2fSWarner Losh DELAY(1000); 933d2155f2fSWarner Losh 934d2155f2fSWarner Losh /* 935d2155f2fSWarner Losh * If this is a NetSemi chip, make sure to clear 936d2155f2fSWarner Losh * PME mode. 937d2155f2fSWarner Losh */ 938d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 939d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS); 940d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_CLKRUN, 0); 941*0af3989bSPyun YongHyeon } else { 942*0af3989bSPyun YongHyeon /* Disable WOL functions. */ 943*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, SIS_PWRMAN_CTL, 0); 944d2155f2fSWarner Losh } 945d2155f2fSWarner Losh } 946d2155f2fSWarner Losh 947d2155f2fSWarner Losh /* 948d2155f2fSWarner Losh * Probe for an SiS chip. Check the PCI vendor and device 949d2155f2fSWarner Losh * IDs against our list and return a device name if we find a match. 950d2155f2fSWarner Losh */ 951d2155f2fSWarner Losh static int 952d2155f2fSWarner Losh sis_probe(device_t dev) 953d2155f2fSWarner Losh { 954d2155f2fSWarner Losh struct sis_type *t; 955d2155f2fSWarner Losh 956d2155f2fSWarner Losh t = sis_devs; 957d2155f2fSWarner Losh 958d2155f2fSWarner Losh while (t->sis_name != NULL) { 959d2155f2fSWarner Losh if ((pci_get_vendor(dev) == t->sis_vid) && 960d2155f2fSWarner Losh (pci_get_device(dev) == t->sis_did)) { 961d2155f2fSWarner Losh device_set_desc(dev, t->sis_name); 962d2155f2fSWarner Losh return (BUS_PROBE_DEFAULT); 963d2155f2fSWarner Losh } 964d2155f2fSWarner Losh t++; 965d2155f2fSWarner Losh } 966d2155f2fSWarner Losh 967d2155f2fSWarner Losh return (ENXIO); 968d2155f2fSWarner Losh } 969d2155f2fSWarner Losh 970d2155f2fSWarner Losh /* 971d2155f2fSWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 972d2155f2fSWarner Losh * setup and ethernet/BPF attach. 973d2155f2fSWarner Losh */ 974d2155f2fSWarner Losh static int 975d2155f2fSWarner Losh sis_attach(device_t dev) 976d2155f2fSWarner Losh { 977d2155f2fSWarner Losh u_char eaddr[ETHER_ADDR_LEN]; 978d2155f2fSWarner Losh struct sis_softc *sc; 979d2155f2fSWarner Losh struct ifnet *ifp; 980*0af3989bSPyun YongHyeon int error = 0, pmc, waittime = 0; 981d2155f2fSWarner Losh 982d2155f2fSWarner Losh waittime = 0; 983d2155f2fSWarner Losh sc = device_get_softc(dev); 984d2155f2fSWarner Losh 985d2155f2fSWarner Losh sc->sis_dev = dev; 986d2155f2fSWarner Losh 987d2155f2fSWarner Losh mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 988d2155f2fSWarner Losh MTX_DEF); 989d2155f2fSWarner Losh callout_init_mtx(&sc->sis_stat_ch, &sc->sis_mtx, 0); 990d2155f2fSWarner Losh 991d2155f2fSWarner Losh if (pci_get_device(dev) == SIS_DEVICEID_900) 992d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_900; 993d2155f2fSWarner Losh if (pci_get_device(dev) == SIS_DEVICEID_7016) 994d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_7016; 995d2155f2fSWarner Losh if (pci_get_vendor(dev) == NS_VENDORID) 996d2155f2fSWarner Losh sc->sis_type = SIS_TYPE_83815; 997d2155f2fSWarner Losh 998d2155f2fSWarner Losh sc->sis_rev = pci_read_config(dev, PCIR_REVID, 1); 999d2155f2fSWarner Losh /* 1000d2155f2fSWarner Losh * Map control/status registers. 1001d2155f2fSWarner Losh */ 1002d2155f2fSWarner Losh pci_enable_busmaster(dev); 1003d2155f2fSWarner Losh 1004d2155f2fSWarner Losh error = bus_alloc_resources(dev, sis_res_spec, sc->sis_res); 1005d2155f2fSWarner Losh if (error) { 1006d2155f2fSWarner Losh device_printf(dev, "couldn't allocate resources\n"); 1007d2155f2fSWarner Losh goto fail; 1008d2155f2fSWarner Losh } 1009d2155f2fSWarner Losh 1010d2155f2fSWarner Losh /* Reset the adapter. */ 1011d2155f2fSWarner Losh sis_reset(sc); 1012d2155f2fSWarner Losh 1013d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_900 && 1014d2155f2fSWarner Losh (sc->sis_rev == SIS_REV_635 || 1015d2155f2fSWarner Losh sc->sis_rev == SIS_REV_900B)) { 1016d2155f2fSWarner Losh SIO_SET(SIS_CFG_RND_CNT); 1017d2155f2fSWarner Losh SIO_SET(SIS_CFG_PERR_DETECT); 1018d2155f2fSWarner Losh } 1019d2155f2fSWarner Losh 1020d2155f2fSWarner Losh /* 1021d2155f2fSWarner Losh * Get station address from the EEPROM. 1022d2155f2fSWarner Losh */ 1023d2155f2fSWarner Losh switch (pci_get_vendor(dev)) { 1024d2155f2fSWarner Losh case NS_VENDORID: 1025d2155f2fSWarner Losh sc->sis_srr = CSR_READ_4(sc, NS_SRR); 1026d2155f2fSWarner Losh 1027d2155f2fSWarner Losh /* We can't update the device description, so spew */ 1028d2155f2fSWarner Losh if (sc->sis_srr == NS_SRR_15C) 1029d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83815C\n"); 1030d2155f2fSWarner Losh else if (sc->sis_srr == NS_SRR_15D) 1031d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83815D\n"); 1032d2155f2fSWarner Losh else if (sc->sis_srr == NS_SRR_16A) 1033d2155f2fSWarner Losh device_printf(dev, "Silicon Revision: DP83816A\n"); 1034d2155f2fSWarner Losh else 1035d2155f2fSWarner Losh device_printf(dev, "Silicon Revision %x\n", sc->sis_srr); 1036d2155f2fSWarner Losh 1037d2155f2fSWarner Losh /* 1038d2155f2fSWarner Losh * Reading the MAC address out of the EEPROM on 1039d2155f2fSWarner Losh * the NatSemi chip takes a bit more work than 1040d2155f2fSWarner Losh * you'd expect. The address spans 4 16-bit words, 1041d2155f2fSWarner Losh * with the first word containing only a single bit. 1042d2155f2fSWarner Losh * You have to shift everything over one bit to 1043d2155f2fSWarner Losh * get it aligned properly. Also, the bits are 1044d2155f2fSWarner Losh * stored backwards (the LSB is really the MSB, 1045d2155f2fSWarner Losh * and so on) so you have to reverse them in order 1046d2155f2fSWarner Losh * to get the MAC address into the form we want. 1047d2155f2fSWarner Losh * Why? Who the hell knows. 1048d2155f2fSWarner Losh */ 1049d2155f2fSWarner Losh { 105091c265b8SPyun YongHyeon uint16_t tmp[4]; 1051d2155f2fSWarner Losh 1052d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&tmp, 1053d2155f2fSWarner Losh NS_EE_NODEADDR, 4, 0); 1054d2155f2fSWarner Losh 1055d2155f2fSWarner Losh /* Shift everything over one bit. */ 1056d2155f2fSWarner Losh tmp[3] = tmp[3] >> 1; 1057d2155f2fSWarner Losh tmp[3] |= tmp[2] << 15; 1058d2155f2fSWarner Losh tmp[2] = tmp[2] >> 1; 1059d2155f2fSWarner Losh tmp[2] |= tmp[1] << 15; 1060d2155f2fSWarner Losh tmp[1] = tmp[1] >> 1; 1061d2155f2fSWarner Losh tmp[1] |= tmp[0] << 15; 1062d2155f2fSWarner Losh 1063d2155f2fSWarner Losh /* Now reverse all the bits. */ 1064d2155f2fSWarner Losh tmp[3] = sis_reverse(tmp[3]); 1065d2155f2fSWarner Losh tmp[2] = sis_reverse(tmp[2]); 1066d2155f2fSWarner Losh tmp[1] = sis_reverse(tmp[1]); 1067d2155f2fSWarner Losh 106874e8a323SPyun YongHyeon eaddr[0] = (tmp[1] >> 0) & 0xFF; 106974e8a323SPyun YongHyeon eaddr[1] = (tmp[1] >> 8) & 0xFF; 107074e8a323SPyun YongHyeon eaddr[2] = (tmp[2] >> 0) & 0xFF; 107174e8a323SPyun YongHyeon eaddr[3] = (tmp[2] >> 8) & 0xFF; 107274e8a323SPyun YongHyeon eaddr[4] = (tmp[3] >> 0) & 0xFF; 107374e8a323SPyun YongHyeon eaddr[5] = (tmp[3] >> 8) & 0xFF; 1074d2155f2fSWarner Losh } 1075d2155f2fSWarner Losh break; 1076d2155f2fSWarner Losh case SIS_VENDORID: 1077d2155f2fSWarner Losh default: 1078d2155f2fSWarner Losh #if defined(__i386__) || defined(__amd64__) 1079d2155f2fSWarner Losh /* 1080d2155f2fSWarner Losh * If this is a SiS 630E chipset with an embedded 1081d2155f2fSWarner Losh * SiS 900 controller, we have to read the MAC address 1082d2155f2fSWarner Losh * from the APC CMOS RAM. Our method for doing this 1083d2155f2fSWarner Losh * is very ugly since we have to reach out and grab 1084d2155f2fSWarner Losh * ahold of hardware for which we cannot properly 1085d2155f2fSWarner Losh * allocate resources. This code is only compiled on 1086d2155f2fSWarner Losh * the i386 architecture since the SiS 630E chipset 1087d2155f2fSWarner Losh * is for x86 motherboards only. Note that there are 1088d2155f2fSWarner Losh * a lot of magic numbers in this hack. These are 1089d2155f2fSWarner Losh * taken from SiS's Linux driver. I'd like to replace 1090d2155f2fSWarner Losh * them with proper symbolic definitions, but that 1091d2155f2fSWarner Losh * requires some datasheets that I don't have access 1092d2155f2fSWarner Losh * to at the moment. 1093d2155f2fSWarner Losh */ 1094d2155f2fSWarner Losh if (sc->sis_rev == SIS_REV_630S || 1095d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630E || 1096d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630EA1) 1097d2155f2fSWarner Losh sis_read_cmos(sc, dev, (caddr_t)&eaddr, 0x9, 6); 1098d2155f2fSWarner Losh 1099d2155f2fSWarner Losh else if (sc->sis_rev == SIS_REV_635 || 1100d2155f2fSWarner Losh sc->sis_rev == SIS_REV_630ET) 1101d2155f2fSWarner Losh sis_read_mac(sc, dev, (caddr_t)&eaddr); 1102d2155f2fSWarner Losh else if (sc->sis_rev == SIS_REV_96x) { 1103d2155f2fSWarner Losh /* Allow to read EEPROM from LAN. It is shared 1104d2155f2fSWarner Losh * between a 1394 controller and the NIC and each 1105d2155f2fSWarner Losh * time we access it, we need to set SIS_EECMD_REQ. 1106d2155f2fSWarner Losh */ 1107d2155f2fSWarner Losh SIO_SET(SIS_EECMD_REQ); 1108d2155f2fSWarner Losh for (waittime = 0; waittime < SIS_TIMEOUT; 1109d2155f2fSWarner Losh waittime++) { 1110d2155f2fSWarner Losh /* Force EEPROM to idle state. */ 1111d2155f2fSWarner Losh sis_eeprom_idle(sc); 1112d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECMD_GNT) { 1113d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&eaddr, 1114d2155f2fSWarner Losh SIS_EE_NODEADDR, 3, 0); 1115d2155f2fSWarner Losh break; 1116d2155f2fSWarner Losh } 1117d2155f2fSWarner Losh DELAY(1); 1118d2155f2fSWarner Losh } 1119d2155f2fSWarner Losh /* 1120d2155f2fSWarner Losh * Set SIS_EECTL_CLK to high, so a other master 1121d2155f2fSWarner Losh * can operate on the i2c bus. 1122d2155f2fSWarner Losh */ 1123d2155f2fSWarner Losh SIO_SET(SIS_EECTL_CLK); 1124d2155f2fSWarner Losh /* Refuse EEPROM access by LAN */ 1125d2155f2fSWarner Losh SIO_SET(SIS_EECMD_DONE); 1126d2155f2fSWarner Losh } else 1127d2155f2fSWarner Losh #endif 1128d2155f2fSWarner Losh sis_read_eeprom(sc, (caddr_t)&eaddr, 1129d2155f2fSWarner Losh SIS_EE_NODEADDR, 3, 0); 1130d2155f2fSWarner Losh break; 1131d2155f2fSWarner Losh } 1132d2155f2fSWarner Losh 113394222398SPyun YongHyeon sis_add_sysctls(sc); 113494222398SPyun YongHyeon 1135a629f2b1SPyun YongHyeon /* Allocate DMA'able memory. */ 1136a629f2b1SPyun YongHyeon if ((error = sis_dma_alloc(sc)) != 0) 1137d2155f2fSWarner Losh goto fail; 1138d2155f2fSWarner Losh 1139d2155f2fSWarner Losh ifp = sc->sis_ifp = if_alloc(IFT_ETHER); 1140d2155f2fSWarner Losh if (ifp == NULL) { 1141d2155f2fSWarner Losh device_printf(dev, "can not if_alloc()\n"); 1142d2155f2fSWarner Losh error = ENOSPC; 1143d2155f2fSWarner Losh goto fail; 1144d2155f2fSWarner Losh } 1145d2155f2fSWarner Losh ifp->if_softc = sc; 1146d2155f2fSWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1147d2155f2fSWarner Losh ifp->if_mtu = ETHERMTU; 1148d2155f2fSWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1149d2155f2fSWarner Losh ifp->if_ioctl = sis_ioctl; 1150d2155f2fSWarner Losh ifp->if_start = sis_start; 1151d2155f2fSWarner Losh ifp->if_init = sis_init; 1152d2155f2fSWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, SIS_TX_LIST_CNT - 1); 1153d2155f2fSWarner Losh ifp->if_snd.ifq_drv_maxlen = SIS_TX_LIST_CNT - 1; 1154d2155f2fSWarner Losh IFQ_SET_READY(&ifp->if_snd); 1155d2155f2fSWarner Losh 1156*0af3989bSPyun YongHyeon if (pci_find_extcap(sc->sis_dev, PCIY_PMG, &pmc) == 0) { 1157*0af3989bSPyun YongHyeon if (sc->sis_type == SIS_TYPE_83815) 1158*0af3989bSPyun YongHyeon ifp->if_capabilities |= IFCAP_WOL; 1159*0af3989bSPyun YongHyeon else 1160*0af3989bSPyun YongHyeon ifp->if_capabilities |= IFCAP_WOL_MAGIC; 1161*0af3989bSPyun YongHyeon ifp->if_capenable = ifp->if_capabilities; 1162*0af3989bSPyun YongHyeon } 1163*0af3989bSPyun YongHyeon 1164d2155f2fSWarner Losh /* 1165d2155f2fSWarner Losh * Do MII setup. 1166d2155f2fSWarner Losh */ 1167d2155f2fSWarner Losh if (mii_phy_probe(dev, &sc->sis_miibus, 1168d2155f2fSWarner Losh sis_ifmedia_upd, sis_ifmedia_sts)) { 1169d2155f2fSWarner Losh device_printf(dev, "MII without any PHY!\n"); 1170d2155f2fSWarner Losh error = ENXIO; 1171d2155f2fSWarner Losh goto fail; 1172d2155f2fSWarner Losh } 1173d2155f2fSWarner Losh 1174d2155f2fSWarner Losh /* 1175d2155f2fSWarner Losh * Call MI attach routine. 1176d2155f2fSWarner Losh */ 1177d2155f2fSWarner Losh ether_ifattach(ifp, eaddr); 1178d2155f2fSWarner Losh 1179d2155f2fSWarner Losh /* 1180d2155f2fSWarner Losh * Tell the upper layer(s) we support long frames. 1181d2155f2fSWarner Losh */ 1182d2155f2fSWarner Losh ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 1183d2155f2fSWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 1184d2155f2fSWarner Losh ifp->if_capenable = ifp->if_capabilities; 1185d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1186d2155f2fSWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1187d2155f2fSWarner Losh #endif 1188d2155f2fSWarner Losh 1189d2155f2fSWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1190d2155f2fSWarner Losh error = bus_setup_intr(dev, sc->sis_res[1], INTR_TYPE_NET | INTR_MPSAFE, 1191d2155f2fSWarner Losh NULL, sis_intr, sc, &sc->sis_intrhand); 1192d2155f2fSWarner Losh 1193d2155f2fSWarner Losh if (error) { 1194d2155f2fSWarner Losh device_printf(dev, "couldn't set up irq\n"); 1195d2155f2fSWarner Losh ether_ifdetach(ifp); 1196d2155f2fSWarner Losh goto fail; 1197d2155f2fSWarner Losh } 1198d2155f2fSWarner Losh 1199d2155f2fSWarner Losh fail: 1200d2155f2fSWarner Losh if (error) 1201d2155f2fSWarner Losh sis_detach(dev); 1202d2155f2fSWarner Losh 1203d2155f2fSWarner Losh return (error); 1204d2155f2fSWarner Losh } 1205d2155f2fSWarner Losh 1206d2155f2fSWarner Losh /* 1207d2155f2fSWarner Losh * Shutdown hardware and free up resources. This can be called any 1208d2155f2fSWarner Losh * time after the mutex has been initialized. It is called in both 1209d2155f2fSWarner Losh * the error case in attach and the normal detach case so it needs 1210d2155f2fSWarner Losh * to be careful about only freeing resources that have actually been 1211d2155f2fSWarner Losh * allocated. 1212d2155f2fSWarner Losh */ 1213d2155f2fSWarner Losh static int 1214d2155f2fSWarner Losh sis_detach(device_t dev) 1215d2155f2fSWarner Losh { 1216d2155f2fSWarner Losh struct sis_softc *sc; 1217d2155f2fSWarner Losh struct ifnet *ifp; 1218d2155f2fSWarner Losh 1219d2155f2fSWarner Losh sc = device_get_softc(dev); 1220d2155f2fSWarner Losh KASSERT(mtx_initialized(&sc->sis_mtx), ("sis mutex not initialized")); 1221d2155f2fSWarner Losh ifp = sc->sis_ifp; 1222d2155f2fSWarner Losh 1223d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1224d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1225d2155f2fSWarner Losh ether_poll_deregister(ifp); 1226d2155f2fSWarner Losh #endif 1227d2155f2fSWarner Losh 1228d2155f2fSWarner Losh /* These should only be active if attach succeeded. */ 1229d2155f2fSWarner Losh if (device_is_attached(dev)) { 1230d2155f2fSWarner Losh SIS_LOCK(sc); 1231d2155f2fSWarner Losh sis_stop(sc); 1232d2155f2fSWarner Losh SIS_UNLOCK(sc); 1233d2155f2fSWarner Losh callout_drain(&sc->sis_stat_ch); 1234d2155f2fSWarner Losh ether_ifdetach(ifp); 1235d2155f2fSWarner Losh } 1236d2155f2fSWarner Losh if (sc->sis_miibus) 1237d2155f2fSWarner Losh device_delete_child(dev, sc->sis_miibus); 1238d2155f2fSWarner Losh bus_generic_detach(dev); 1239d2155f2fSWarner Losh 1240d2155f2fSWarner Losh if (sc->sis_intrhand) 1241d2155f2fSWarner Losh bus_teardown_intr(dev, sc->sis_res[1], sc->sis_intrhand); 1242d2155f2fSWarner Losh bus_release_resources(dev, sis_res_spec, sc->sis_res); 1243d2155f2fSWarner Losh 1244d2155f2fSWarner Losh if (ifp) 1245d2155f2fSWarner Losh if_free(ifp); 1246d2155f2fSWarner Losh 1247a629f2b1SPyun YongHyeon sis_dma_free(sc); 1248d2155f2fSWarner Losh 1249d2155f2fSWarner Losh mtx_destroy(&sc->sis_mtx); 1250d2155f2fSWarner Losh 1251d2155f2fSWarner Losh return (0); 1252d2155f2fSWarner Losh } 1253d2155f2fSWarner Losh 1254a629f2b1SPyun YongHyeon struct sis_dmamap_arg { 1255a629f2b1SPyun YongHyeon bus_addr_t sis_busaddr; 1256a629f2b1SPyun YongHyeon }; 1257a629f2b1SPyun YongHyeon 1258a629f2b1SPyun YongHyeon static void 1259a629f2b1SPyun YongHyeon sis_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1260a629f2b1SPyun YongHyeon { 1261a629f2b1SPyun YongHyeon struct sis_dmamap_arg *ctx; 1262a629f2b1SPyun YongHyeon 1263a629f2b1SPyun YongHyeon if (error != 0) 1264a629f2b1SPyun YongHyeon return; 1265a629f2b1SPyun YongHyeon 1266a629f2b1SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1267a629f2b1SPyun YongHyeon 1268a629f2b1SPyun YongHyeon ctx = (struct sis_dmamap_arg *)arg; 1269a629f2b1SPyun YongHyeon ctx->sis_busaddr = segs[0].ds_addr; 1270a629f2b1SPyun YongHyeon } 1271a629f2b1SPyun YongHyeon 1272a629f2b1SPyun YongHyeon static int 1273a629f2b1SPyun YongHyeon sis_dma_ring_alloc(struct sis_softc *sc, bus_size_t alignment, 1274a629f2b1SPyun YongHyeon bus_size_t maxsize, bus_dma_tag_t *tag, uint8_t **ring, bus_dmamap_t *map, 1275a629f2b1SPyun YongHyeon bus_addr_t *paddr, const char *msg) 1276a629f2b1SPyun YongHyeon { 1277a629f2b1SPyun YongHyeon struct sis_dmamap_arg ctx; 1278a629f2b1SPyun YongHyeon int error; 1279a629f2b1SPyun YongHyeon 1280a629f2b1SPyun YongHyeon error = bus_dma_tag_create(sc->sis_parent_tag, alignment, 0, 1281a629f2b1SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, 1, 1282a629f2b1SPyun YongHyeon maxsize, 0, NULL, NULL, tag); 1283a629f2b1SPyun YongHyeon if (error != 0) { 1284a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1285a629f2b1SPyun YongHyeon "could not create %s dma tag\n", msg); 1286a629f2b1SPyun YongHyeon return (ENOMEM); 1287a629f2b1SPyun YongHyeon } 1288a629f2b1SPyun YongHyeon /* Allocate DMA'able memory for ring. */ 1289a629f2b1SPyun YongHyeon error = bus_dmamem_alloc(*tag, (void **)ring, 1290a629f2b1SPyun YongHyeon BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, map); 1291a629f2b1SPyun YongHyeon if (error != 0) { 1292a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1293a629f2b1SPyun YongHyeon "could not allocate DMA'able memory for %s\n", msg); 1294a629f2b1SPyun YongHyeon return (ENOMEM); 1295a629f2b1SPyun YongHyeon } 1296a629f2b1SPyun YongHyeon /* Load the address of the ring. */ 1297a629f2b1SPyun YongHyeon ctx.sis_busaddr = 0; 1298a629f2b1SPyun YongHyeon error = bus_dmamap_load(*tag, *map, *ring, maxsize, sis_dmamap_cb, 1299a629f2b1SPyun YongHyeon &ctx, BUS_DMA_NOWAIT); 1300a629f2b1SPyun YongHyeon if (error != 0) { 1301a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1302a629f2b1SPyun YongHyeon "could not load DMA'able memory for %s\n", msg); 1303a629f2b1SPyun YongHyeon return (ENOMEM); 1304a629f2b1SPyun YongHyeon } 1305a629f2b1SPyun YongHyeon *paddr = ctx.sis_busaddr; 1306a629f2b1SPyun YongHyeon return (0); 1307a629f2b1SPyun YongHyeon } 1308a629f2b1SPyun YongHyeon 1309a629f2b1SPyun YongHyeon static int 1310a629f2b1SPyun YongHyeon sis_dma_alloc(struct sis_softc *sc) 1311a629f2b1SPyun YongHyeon { 1312a629f2b1SPyun YongHyeon struct sis_rxdesc *rxd; 1313a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 1314a629f2b1SPyun YongHyeon int error, i; 1315a629f2b1SPyun YongHyeon 1316a629f2b1SPyun YongHyeon /* Allocate the parent bus DMA tag appropriate for PCI. */ 1317a629f2b1SPyun YongHyeon error = bus_dma_tag_create(bus_get_dma_tag(sc->sis_dev), 1318a629f2b1SPyun YongHyeon 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, 1319a629f2b1SPyun YongHyeon NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 1320a629f2b1SPyun YongHyeon 0, NULL, NULL, &sc->sis_parent_tag); 1321a629f2b1SPyun YongHyeon if (error != 0) { 1322a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1323a629f2b1SPyun YongHyeon "could not allocate parent dma tag\n"); 1324a629f2b1SPyun YongHyeon return (ENOMEM); 1325a629f2b1SPyun YongHyeon } 1326a629f2b1SPyun YongHyeon 1327a629f2b1SPyun YongHyeon /* Create RX ring. */ 1328a629f2b1SPyun YongHyeon error = sis_dma_ring_alloc(sc, SIS_DESC_ALIGN, SIS_RX_LIST_SZ, 1329a629f2b1SPyun YongHyeon &sc->sis_rx_list_tag, (uint8_t **)&sc->sis_rx_list, 1330a629f2b1SPyun YongHyeon &sc->sis_rx_list_map, &sc->sis_rx_paddr, "RX ring"); 1331a629f2b1SPyun YongHyeon if (error) 1332a629f2b1SPyun YongHyeon return (error); 1333a629f2b1SPyun YongHyeon 1334a629f2b1SPyun YongHyeon /* Create TX ring. */ 1335a629f2b1SPyun YongHyeon error = sis_dma_ring_alloc(sc, SIS_DESC_ALIGN, SIS_TX_LIST_SZ, 1336a629f2b1SPyun YongHyeon &sc->sis_tx_list_tag, (uint8_t **)&sc->sis_tx_list, 1337a629f2b1SPyun YongHyeon &sc->sis_tx_list_map, &sc->sis_tx_paddr, "TX ring"); 1338a629f2b1SPyun YongHyeon if (error) 1339a629f2b1SPyun YongHyeon return (error); 1340a629f2b1SPyun YongHyeon 1341a629f2b1SPyun YongHyeon /* Create tag for RX mbufs. */ 1342a629f2b1SPyun YongHyeon error = bus_dma_tag_create(sc->sis_parent_tag, SIS_RX_BUF_ALIGN, 0, 1343a629f2b1SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, 1344a629f2b1SPyun YongHyeon MCLBYTES, 0, NULL, NULL, &sc->sis_rx_tag); 1345a629f2b1SPyun YongHyeon if (error) { 1346a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, "could not allocate RX dma tag\n"); 1347a629f2b1SPyun YongHyeon return (error); 1348a629f2b1SPyun YongHyeon } 1349a629f2b1SPyun YongHyeon 1350a629f2b1SPyun YongHyeon /* Create tag for TX mbufs. */ 1351a629f2b1SPyun YongHyeon error = bus_dma_tag_create(sc->sis_parent_tag, 1, 0, 1352a629f2b1SPyun YongHyeon BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 1353a629f2b1SPyun YongHyeon MCLBYTES * SIS_MAXTXSEGS, SIS_MAXTXSEGS, MCLBYTES, 0, NULL, NULL, 1354a629f2b1SPyun YongHyeon &sc->sis_tx_tag); 1355a629f2b1SPyun YongHyeon if (error) { 1356a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, "could not allocate TX dma tag\n"); 1357a629f2b1SPyun YongHyeon return (error); 1358a629f2b1SPyun YongHyeon } 1359a629f2b1SPyun YongHyeon 1360a629f2b1SPyun YongHyeon /* Create DMA maps for RX buffers. */ 1361a629f2b1SPyun YongHyeon error = bus_dmamap_create(sc->sis_rx_tag, 0, &sc->sis_rx_sparemap); 1362a629f2b1SPyun YongHyeon if (error) { 1363a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1364a629f2b1SPyun YongHyeon "can't create spare DMA map for RX\n"); 1365a629f2b1SPyun YongHyeon return (error); 1366a629f2b1SPyun YongHyeon } 1367a629f2b1SPyun YongHyeon for (i = 0; i < SIS_RX_LIST_CNT; i++) { 1368a629f2b1SPyun YongHyeon rxd = &sc->sis_rxdesc[i]; 1369a629f2b1SPyun YongHyeon rxd->rx_m = NULL; 1370a629f2b1SPyun YongHyeon error = bus_dmamap_create(sc->sis_rx_tag, 0, &rxd->rx_dmamap); 1371a629f2b1SPyun YongHyeon if (error) { 1372a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1373a629f2b1SPyun YongHyeon "can't create DMA map for RX\n"); 1374a629f2b1SPyun YongHyeon return (error); 1375a629f2b1SPyun YongHyeon } 1376a629f2b1SPyun YongHyeon } 1377a629f2b1SPyun YongHyeon 1378a629f2b1SPyun YongHyeon /* Create DMA maps for TX buffers. */ 1379a629f2b1SPyun YongHyeon for (i = 0; i < SIS_TX_LIST_CNT; i++) { 1380a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[i]; 1381a629f2b1SPyun YongHyeon txd->tx_m = NULL; 1382a629f2b1SPyun YongHyeon error = bus_dmamap_create(sc->sis_tx_tag, 0, &txd->tx_dmamap); 1383a629f2b1SPyun YongHyeon if (error) { 1384a629f2b1SPyun YongHyeon device_printf(sc->sis_dev, 1385a629f2b1SPyun YongHyeon "can't create DMA map for TX\n"); 1386a629f2b1SPyun YongHyeon return (error); 1387a629f2b1SPyun YongHyeon } 1388a629f2b1SPyun YongHyeon } 1389a629f2b1SPyun YongHyeon 1390a629f2b1SPyun YongHyeon return (0); 1391a629f2b1SPyun YongHyeon } 1392a629f2b1SPyun YongHyeon 1393a629f2b1SPyun YongHyeon static void 1394a629f2b1SPyun YongHyeon sis_dma_free(struct sis_softc *sc) 1395a629f2b1SPyun YongHyeon { 1396a629f2b1SPyun YongHyeon struct sis_rxdesc *rxd; 1397a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 1398a629f2b1SPyun YongHyeon int i; 1399a629f2b1SPyun YongHyeon 1400a629f2b1SPyun YongHyeon /* Destroy DMA maps for RX buffers. */ 1401a629f2b1SPyun YongHyeon for (i = 0; i < SIS_RX_LIST_CNT; i++) { 1402a629f2b1SPyun YongHyeon rxd = &sc->sis_rxdesc[i]; 1403a629f2b1SPyun YongHyeon if (rxd->rx_dmamap) 1404a629f2b1SPyun YongHyeon bus_dmamap_destroy(sc->sis_rx_tag, rxd->rx_dmamap); 1405a629f2b1SPyun YongHyeon } 1406a629f2b1SPyun YongHyeon if (sc->sis_rx_sparemap) 1407a629f2b1SPyun YongHyeon bus_dmamap_destroy(sc->sis_rx_tag, sc->sis_rx_sparemap); 1408a629f2b1SPyun YongHyeon 1409a629f2b1SPyun YongHyeon /* Destroy DMA maps for TX buffers. */ 1410a629f2b1SPyun YongHyeon for (i = 0; i < SIS_TX_LIST_CNT; i++) { 1411a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[i]; 1412a629f2b1SPyun YongHyeon if (txd->tx_dmamap) 1413a629f2b1SPyun YongHyeon bus_dmamap_destroy(sc->sis_tx_tag, txd->tx_dmamap); 1414a629f2b1SPyun YongHyeon } 1415a629f2b1SPyun YongHyeon 1416a629f2b1SPyun YongHyeon if (sc->sis_rx_tag) 1417a629f2b1SPyun YongHyeon bus_dma_tag_destroy(sc->sis_rx_tag); 1418a629f2b1SPyun YongHyeon if (sc->sis_tx_tag) 1419a629f2b1SPyun YongHyeon bus_dma_tag_destroy(sc->sis_tx_tag); 1420a629f2b1SPyun YongHyeon 1421a629f2b1SPyun YongHyeon /* Destroy RX ring. */ 1422a629f2b1SPyun YongHyeon if (sc->sis_rx_list_map) 1423a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_rx_list_tag, sc->sis_rx_list_map); 1424a629f2b1SPyun YongHyeon if (sc->sis_rx_list_map && sc->sis_rx_list) 1425a629f2b1SPyun YongHyeon bus_dmamem_free(sc->sis_rx_list_tag, sc->sis_rx_list, 1426a629f2b1SPyun YongHyeon sc->sis_rx_list_map); 1427a629f2b1SPyun YongHyeon 1428a629f2b1SPyun YongHyeon if (sc->sis_rx_list_tag) 1429a629f2b1SPyun YongHyeon bus_dma_tag_destroy(sc->sis_rx_list_tag); 1430a629f2b1SPyun YongHyeon 1431a629f2b1SPyun YongHyeon /* Destroy TX ring. */ 1432a629f2b1SPyun YongHyeon if (sc->sis_tx_list_map) 1433a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_tx_list_tag, sc->sis_tx_list_map); 1434a629f2b1SPyun YongHyeon 1435a629f2b1SPyun YongHyeon if (sc->sis_tx_list_map && sc->sis_tx_list) 1436a629f2b1SPyun YongHyeon bus_dmamem_free(sc->sis_tx_list_tag, sc->sis_tx_list, 1437a629f2b1SPyun YongHyeon sc->sis_tx_list_map); 1438a629f2b1SPyun YongHyeon 1439a629f2b1SPyun YongHyeon if (sc->sis_tx_list_tag) 1440a629f2b1SPyun YongHyeon bus_dma_tag_destroy(sc->sis_tx_list_tag); 1441a629f2b1SPyun YongHyeon 1442a629f2b1SPyun YongHyeon /* Destroy the parent tag. */ 1443a629f2b1SPyun YongHyeon if (sc->sis_parent_tag) 1444a629f2b1SPyun YongHyeon bus_dma_tag_destroy(sc->sis_parent_tag); 1445a629f2b1SPyun YongHyeon } 1446a629f2b1SPyun YongHyeon 1447d2155f2fSWarner Losh /* 1448d2155f2fSWarner Losh * Initialize the TX and RX descriptors and allocate mbufs for them. Note that 1449d2155f2fSWarner Losh * we arrange the descriptors in a closed ring, so that the last descriptor 1450d2155f2fSWarner Losh * points back to the first. 1451d2155f2fSWarner Losh */ 1452d2155f2fSWarner Losh static int 1453d2155f2fSWarner Losh sis_ring_init(struct sis_softc *sc) 1454d2155f2fSWarner Losh { 1455a629f2b1SPyun YongHyeon struct sis_rxdesc *rxd; 1456a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 1457a629f2b1SPyun YongHyeon bus_addr_t next; 1458a629f2b1SPyun YongHyeon int error, i; 1459d2155f2fSWarner Losh 1460a629f2b1SPyun YongHyeon bzero(&sc->sis_tx_list[0], SIS_TX_LIST_SZ); 1461a629f2b1SPyun YongHyeon for (i = 0; i < SIS_TX_LIST_CNT; i++) { 1462a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[i]; 1463a629f2b1SPyun YongHyeon txd->tx_m = NULL; 1464a629f2b1SPyun YongHyeon if (i == SIS_TX_LIST_CNT - 1) 1465a629f2b1SPyun YongHyeon next = SIS_TX_RING_ADDR(sc, 0); 1466d2155f2fSWarner Losh else 1467a629f2b1SPyun YongHyeon next = SIS_TX_RING_ADDR(sc, i + 1); 1468a629f2b1SPyun YongHyeon sc->sis_tx_list[i].sis_next = htole32(SIS_ADDR_LO(next)); 1469d2155f2fSWarner Losh } 1470d2155f2fSWarner Losh sc->sis_tx_prod = sc->sis_tx_cons = sc->sis_tx_cnt = 0; 1471a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_list_tag, sc->sis_tx_list_map, 1472a629f2b1SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1473d2155f2fSWarner Losh 1474a629f2b1SPyun YongHyeon sc->sis_rx_cons = 0; 1475a629f2b1SPyun YongHyeon bzero(&sc->sis_rx_list[0], SIS_RX_LIST_SZ); 1476a629f2b1SPyun YongHyeon for (i = 0; i < SIS_RX_LIST_CNT; i++) { 1477a629f2b1SPyun YongHyeon rxd = &sc->sis_rxdesc[i]; 1478a629f2b1SPyun YongHyeon rxd->rx_desc = &sc->sis_rx_list[i]; 1479a629f2b1SPyun YongHyeon if (i == SIS_RX_LIST_CNT - 1) 1480a629f2b1SPyun YongHyeon next = SIS_RX_RING_ADDR(sc, 0); 1481a629f2b1SPyun YongHyeon else 1482a629f2b1SPyun YongHyeon next = SIS_RX_RING_ADDR(sc, i + 1); 1483a629f2b1SPyun YongHyeon rxd->rx_desc->sis_next = htole32(SIS_ADDR_LO(next)); 1484a629f2b1SPyun YongHyeon error = sis_newbuf(sc, rxd); 1485d2155f2fSWarner Losh if (error) 1486d2155f2fSWarner Losh return (error); 1487d2155f2fSWarner Losh } 1488a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_list_tag, sc->sis_rx_list_map, 1489a629f2b1SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1490d2155f2fSWarner Losh 1491d2155f2fSWarner Losh return (0); 1492d2155f2fSWarner Losh } 1493d2155f2fSWarner Losh 1494d2155f2fSWarner Losh /* 1495d2155f2fSWarner Losh * Initialize an RX descriptor and attach an MBUF cluster. 1496d2155f2fSWarner Losh */ 1497d2155f2fSWarner Losh static int 1498a629f2b1SPyun YongHyeon sis_newbuf(struct sis_softc *sc, struct sis_rxdesc *rxd) 1499d2155f2fSWarner Losh { 1500a629f2b1SPyun YongHyeon struct mbuf *m; 1501a629f2b1SPyun YongHyeon bus_dma_segment_t segs[1]; 1502a629f2b1SPyun YongHyeon bus_dmamap_t map; 1503a629f2b1SPyun YongHyeon int nsegs; 1504d2155f2fSWarner Losh 1505d2155f2fSWarner Losh m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1506d2155f2fSWarner Losh if (m == NULL) 1507d2155f2fSWarner Losh return (ENOBUFS); 1508a629f2b1SPyun YongHyeon m->m_len = m->m_pkthdr.len = SIS_RXLEN; 1509a629f2b1SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 1510a629f2b1SPyun YongHyeon m_adj(m, SIS_RX_BUF_ALIGN); 1511a629f2b1SPyun YongHyeon #endif 1512d2155f2fSWarner Losh 1513a629f2b1SPyun YongHyeon if (bus_dmamap_load_mbuf_sg(sc->sis_rx_tag, sc->sis_rx_sparemap, m, 1514a629f2b1SPyun YongHyeon segs, &nsegs, 0) != 0) { 1515a629f2b1SPyun YongHyeon m_freem(m); 1516a629f2b1SPyun YongHyeon return (ENOBUFS); 1517a629f2b1SPyun YongHyeon } 1518a629f2b1SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1519d2155f2fSWarner Losh 1520a629f2b1SPyun YongHyeon if (rxd->rx_m != NULL) { 1521a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_tag, rxd->rx_dmamap, 1522a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTREAD); 1523a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_rx_tag, rxd->rx_dmamap); 1524a629f2b1SPyun YongHyeon } 1525a629f2b1SPyun YongHyeon map = rxd->rx_dmamap; 1526a629f2b1SPyun YongHyeon rxd->rx_dmamap = sc->sis_rx_sparemap; 1527a629f2b1SPyun YongHyeon sc->sis_rx_sparemap = map; 1528a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_tag, rxd->rx_dmamap, BUS_DMASYNC_PREREAD); 1529a629f2b1SPyun YongHyeon rxd->rx_m = m; 1530a629f2b1SPyun YongHyeon rxd->rx_desc->sis_cmdsts = htole32(SIS_RXLEN); 1531a629f2b1SPyun YongHyeon rxd->rx_desc->sis_ptr = htole32(SIS_ADDR_LO(segs[0].ds_addr)); 1532d2155f2fSWarner Losh return (0); 1533d2155f2fSWarner Losh } 1534d2155f2fSWarner Losh 1535a629f2b1SPyun YongHyeon static __inline void 1536a629f2b1SPyun YongHyeon sis_discard_rxbuf(struct sis_rxdesc *rxd) 1537a629f2b1SPyun YongHyeon { 1538a629f2b1SPyun YongHyeon 1539a629f2b1SPyun YongHyeon rxd->rx_desc->sis_cmdsts = htole32(SIS_RXLEN); 1540a629f2b1SPyun YongHyeon } 1541a629f2b1SPyun YongHyeon 1542a629f2b1SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 1543a629f2b1SPyun YongHyeon static __inline void 1544a629f2b1SPyun YongHyeon sis_fixup_rx(struct mbuf *m) 1545a629f2b1SPyun YongHyeon { 1546a629f2b1SPyun YongHyeon uint16_t *src, *dst; 1547a629f2b1SPyun YongHyeon int i; 1548a629f2b1SPyun YongHyeon 1549a629f2b1SPyun YongHyeon src = mtod(m, uint16_t *); 1550a629f2b1SPyun YongHyeon dst = src - (SIS_RX_BUF_ALIGN - ETHER_ALIGN) / sizeof(*src); 1551a629f2b1SPyun YongHyeon 1552a629f2b1SPyun YongHyeon for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 1553a629f2b1SPyun YongHyeon *dst++ = *src++; 1554a629f2b1SPyun YongHyeon 1555a629f2b1SPyun YongHyeon m->m_data -= SIS_RX_BUF_ALIGN - ETHER_ALIGN; 1556a629f2b1SPyun YongHyeon } 1557a629f2b1SPyun YongHyeon #endif 1558a629f2b1SPyun YongHyeon 1559d2155f2fSWarner Losh /* 1560d2155f2fSWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 1561d2155f2fSWarner Losh * the higher level protocols. 1562d2155f2fSWarner Losh */ 15631abcdbd1SAttilio Rao static int 1564d2155f2fSWarner Losh sis_rxeof(struct sis_softc *sc) 1565d2155f2fSWarner Losh { 1566a629f2b1SPyun YongHyeon struct mbuf *m; 1567d2155f2fSWarner Losh struct ifnet *ifp; 1568a629f2b1SPyun YongHyeon struct sis_rxdesc *rxd; 1569d2155f2fSWarner Losh struct sis_desc *cur_rx; 1570a629f2b1SPyun YongHyeon int prog, rx_cons, rx_npkts = 0, total_len; 1571a629f2b1SPyun YongHyeon uint32_t rxstat; 1572d2155f2fSWarner Losh 1573d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1574d2155f2fSWarner Losh 1575a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_list_tag, sc->sis_rx_list_map, 1576a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1577a629f2b1SPyun YongHyeon 1578a629f2b1SPyun YongHyeon rx_cons = sc->sis_rx_cons; 1579d2155f2fSWarner Losh ifp = sc->sis_ifp; 1580d2155f2fSWarner Losh 1581a629f2b1SPyun YongHyeon for (prog = 0; (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; 1582a629f2b1SPyun YongHyeon SIS_INC(rx_cons, SIS_RX_LIST_CNT), prog++) { 1583d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1584d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 1585d2155f2fSWarner Losh if (sc->rxcycles <= 0) 1586d2155f2fSWarner Losh break; 1587d2155f2fSWarner Losh sc->rxcycles--; 1588d2155f2fSWarner Losh } 1589d2155f2fSWarner Losh #endif 1590a629f2b1SPyun YongHyeon cur_rx = &sc->sis_rx_list[rx_cons]; 1591a629f2b1SPyun YongHyeon rxstat = le32toh(cur_rx->sis_cmdsts); 1592a629f2b1SPyun YongHyeon if ((rxstat & SIS_CMDSTS_OWN) == 0) 1593a629f2b1SPyun YongHyeon break; 1594a629f2b1SPyun YongHyeon rxd = &sc->sis_rxdesc[rx_cons]; 1595d2155f2fSWarner Losh 1596a629f2b1SPyun YongHyeon total_len = (rxstat & SIS_CMDSTS_BUFLEN) - ETHER_CRC_LEN; 159792483efaSPyun YongHyeon if ((ifp->if_capenable & IFCAP_VLAN_MTU) != 0 && 159892483efaSPyun YongHyeon total_len <= (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN - 159992483efaSPyun YongHyeon ETHER_CRC_LEN)) 160092483efaSPyun YongHyeon rxstat &= ~SIS_RXSTAT_GIANT; 160192483efaSPyun YongHyeon if (SIS_RXSTAT_ERROR(rxstat) != 0) { 1602d2155f2fSWarner Losh ifp->if_ierrors++; 1603d2155f2fSWarner Losh if (rxstat & SIS_RXSTAT_COLL) 1604d2155f2fSWarner Losh ifp->if_collisions++; 1605a629f2b1SPyun YongHyeon sis_discard_rxbuf(rxd); 1606a629f2b1SPyun YongHyeon continue; 1607a629f2b1SPyun YongHyeon } 1608a629f2b1SPyun YongHyeon 1609a629f2b1SPyun YongHyeon /* Add a new receive buffer to the ring. */ 1610a629f2b1SPyun YongHyeon m = rxd->rx_m; 1611a629f2b1SPyun YongHyeon if (sis_newbuf(sc, rxd) != 0) { 1612a629f2b1SPyun YongHyeon ifp->if_iqdrops++; 1613a629f2b1SPyun YongHyeon sis_discard_rxbuf(rxd); 1614d2155f2fSWarner Losh continue; 1615d2155f2fSWarner Losh } 1616d2155f2fSWarner Losh 1617d2155f2fSWarner Losh /* No errors; receive the packet. */ 1618a629f2b1SPyun YongHyeon m->m_pkthdr.len = m->m_len = total_len; 1619a629f2b1SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 1620d2155f2fSWarner Losh /* 1621d2155f2fSWarner Losh * On architectures without alignment problems we try to 1622d2155f2fSWarner Losh * allocate a new buffer for the receive ring, and pass up 1623d2155f2fSWarner Losh * the one where the packet is already, saving the expensive 1624a629f2b1SPyun YongHyeon * copy operation. 1625d2155f2fSWarner Losh */ 1626a629f2b1SPyun YongHyeon sis_fixup_rx(m); 1627d2155f2fSWarner Losh #endif 1628d2155f2fSWarner Losh ifp->if_ipackets++; 1629d2155f2fSWarner Losh m->m_pkthdr.rcvif = ifp; 1630d2155f2fSWarner Losh 1631d2155f2fSWarner Losh SIS_UNLOCK(sc); 1632d2155f2fSWarner Losh (*ifp->if_input)(ifp, m); 1633d2155f2fSWarner Losh SIS_LOCK(sc); 16341abcdbd1SAttilio Rao rx_npkts++; 1635d2155f2fSWarner Losh } 1636d2155f2fSWarner Losh 1637a629f2b1SPyun YongHyeon if (prog > 0) { 1638a629f2b1SPyun YongHyeon sc->sis_rx_cons = rx_cons; 1639a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_list_tag, sc->sis_rx_list_map, 1640a629f2b1SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1641a629f2b1SPyun YongHyeon } 1642a629f2b1SPyun YongHyeon 16431abcdbd1SAttilio Rao return (rx_npkts); 1644d2155f2fSWarner Losh } 1645d2155f2fSWarner Losh 1646d2155f2fSWarner Losh /* 1647d2155f2fSWarner Losh * A frame was downloaded to the chip. It's safe for us to clean up 1648d2155f2fSWarner Losh * the list buffers. 1649d2155f2fSWarner Losh */ 1650d2155f2fSWarner Losh 1651d2155f2fSWarner Losh static void 1652d2155f2fSWarner Losh sis_txeof(struct sis_softc *sc) 1653d2155f2fSWarner Losh { 1654d2155f2fSWarner Losh struct ifnet *ifp; 1655a629f2b1SPyun YongHyeon struct sis_desc *cur_tx; 1656a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 1657a629f2b1SPyun YongHyeon uint32_t cons, txstat; 1658d2155f2fSWarner Losh 1659d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1660a629f2b1SPyun YongHyeon 1661a629f2b1SPyun YongHyeon cons = sc->sis_tx_cons; 1662a629f2b1SPyun YongHyeon if (cons == sc->sis_tx_prod) 1663a629f2b1SPyun YongHyeon return; 1664a629f2b1SPyun YongHyeon 1665d2155f2fSWarner Losh ifp = sc->sis_ifp; 1666a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_list_tag, sc->sis_tx_list_map, 1667a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1668d2155f2fSWarner Losh 1669d2155f2fSWarner Losh /* 1670d2155f2fSWarner Losh * Go through our tx list and free mbufs for those 1671d2155f2fSWarner Losh * frames that have been transmitted. 1672d2155f2fSWarner Losh */ 1673a629f2b1SPyun YongHyeon for (; cons != sc->sis_tx_prod; SIS_INC(cons, SIS_TX_LIST_CNT)) { 1674a629f2b1SPyun YongHyeon cur_tx = &sc->sis_tx_list[cons]; 1675a629f2b1SPyun YongHyeon txstat = le32toh(cur_tx->sis_cmdsts); 1676a629f2b1SPyun YongHyeon if ((txstat & SIS_CMDSTS_OWN) != 0) 1677d2155f2fSWarner Losh break; 1678a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[cons]; 1679a629f2b1SPyun YongHyeon if (txd->tx_m != NULL) { 1680a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_tag, txd->tx_dmamap, 1681a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTWRITE); 1682a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_tx_tag, txd->tx_dmamap); 1683a629f2b1SPyun YongHyeon m_freem(txd->tx_m); 1684a629f2b1SPyun YongHyeon txd->tx_m = NULL; 1685a629f2b1SPyun YongHyeon if ((txstat & SIS_CMDSTS_PKT_OK) != 0) { 1686d2155f2fSWarner Losh ifp->if_opackets++; 1687a629f2b1SPyun YongHyeon ifp->if_collisions += 1688a629f2b1SPyun YongHyeon (txstat & SIS_TXSTAT_COLLCNT) >> 16; 1689a629f2b1SPyun YongHyeon } else { 1690a629f2b1SPyun YongHyeon ifp->if_oerrors++; 1691a629f2b1SPyun YongHyeon if (txstat & SIS_TXSTAT_EXCESSCOLLS) 1692a629f2b1SPyun YongHyeon ifp->if_collisions++; 1693a629f2b1SPyun YongHyeon if (txstat & SIS_TXSTAT_OUTOFWINCOLL) 1694a629f2b1SPyun YongHyeon ifp->if_collisions++; 1695d2155f2fSWarner Losh } 1696d2155f2fSWarner Losh } 1697a629f2b1SPyun YongHyeon sc->sis_tx_cnt--; 1698d2155f2fSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1699d2155f2fSWarner Losh } 1700a629f2b1SPyun YongHyeon sc->sis_tx_cons = cons; 1701a629f2b1SPyun YongHyeon if (sc->sis_tx_cnt == 0) 1702a629f2b1SPyun YongHyeon sc->sis_watchdog_timer = 0; 1703d2155f2fSWarner Losh } 1704d2155f2fSWarner Losh 1705d2155f2fSWarner Losh static void 1706d2155f2fSWarner Losh sis_tick(void *xsc) 1707d2155f2fSWarner Losh { 1708d2155f2fSWarner Losh struct sis_softc *sc; 1709d2155f2fSWarner Losh struct mii_data *mii; 1710d2155f2fSWarner Losh struct ifnet *ifp; 1711d2155f2fSWarner Losh 1712d2155f2fSWarner Losh sc = xsc; 1713d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1714d2155f2fSWarner Losh ifp = sc->sis_ifp; 1715d2155f2fSWarner Losh 1716d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 1717d2155f2fSWarner Losh mii_tick(mii); 1718d2155f2fSWarner Losh sis_watchdog(sc); 171994222398SPyun YongHyeon if ((sc->sis_flags & SIS_FLAG_LINK) == 0) 1720d7b57e79SPyun YongHyeon sis_miibus_statchg(sc->sis_dev); 1721d2155f2fSWarner Losh callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); 1722d2155f2fSWarner Losh } 1723d2155f2fSWarner Losh 1724d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1725d2155f2fSWarner Losh static poll_handler_t sis_poll; 1726d2155f2fSWarner Losh 17271abcdbd1SAttilio Rao static int 1728d2155f2fSWarner Losh sis_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 1729d2155f2fSWarner Losh { 1730d2155f2fSWarner Losh struct sis_softc *sc = ifp->if_softc; 17311abcdbd1SAttilio Rao int rx_npkts = 0; 1732d2155f2fSWarner Losh 1733d2155f2fSWarner Losh SIS_LOCK(sc); 1734d2155f2fSWarner Losh if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1735d2155f2fSWarner Losh SIS_UNLOCK(sc); 17361abcdbd1SAttilio Rao return (rx_npkts); 1737d2155f2fSWarner Losh } 1738d2155f2fSWarner Losh 1739d2155f2fSWarner Losh /* 1740d2155f2fSWarner Losh * On the sis, reading the status register also clears it. 1741d2155f2fSWarner Losh * So before returning to intr mode we must make sure that all 1742d2155f2fSWarner Losh * possible pending sources of interrupts have been served. 1743d2155f2fSWarner Losh * In practice this means run to completion the *eof routines, 1744d2155f2fSWarner Losh * and then call the interrupt routine 1745d2155f2fSWarner Losh */ 1746d2155f2fSWarner Losh sc->rxcycles = count; 17471abcdbd1SAttilio Rao rx_npkts = sis_rxeof(sc); 1748d2155f2fSWarner Losh sis_txeof(sc); 1749d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1750d2155f2fSWarner Losh sis_startl(ifp); 1751d2155f2fSWarner Losh 1752d2155f2fSWarner Losh if (sc->rxcycles > 0 || cmd == POLL_AND_CHECK_STATUS) { 175391c265b8SPyun YongHyeon uint32_t status; 1754d2155f2fSWarner Losh 1755d2155f2fSWarner Losh /* Reading the ISR register clears all interrupts. */ 1756d2155f2fSWarner Losh status = CSR_READ_4(sc, SIS_ISR); 1757d2155f2fSWarner Losh 1758d2155f2fSWarner Losh if (status & (SIS_ISR_RX_ERR|SIS_ISR_RX_OFLOW)) 175962592d91SRebecca Cran ifp->if_ierrors++; 1760d2155f2fSWarner Losh 1761d2155f2fSWarner Losh if (status & (SIS_ISR_RX_IDLE)) 1762d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 1763d2155f2fSWarner Losh 1764d2155f2fSWarner Losh if (status & SIS_ISR_SYSERR) { 1765d199ef7eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1766d2155f2fSWarner Losh sis_initl(sc); 1767d2155f2fSWarner Losh } 1768d2155f2fSWarner Losh } 1769d2155f2fSWarner Losh 1770d2155f2fSWarner Losh SIS_UNLOCK(sc); 17711abcdbd1SAttilio Rao return (rx_npkts); 1772d2155f2fSWarner Losh } 1773d2155f2fSWarner Losh #endif /* DEVICE_POLLING */ 1774d2155f2fSWarner Losh 1775d2155f2fSWarner Losh static void 1776d2155f2fSWarner Losh sis_intr(void *arg) 1777d2155f2fSWarner Losh { 1778d2155f2fSWarner Losh struct sis_softc *sc; 1779d2155f2fSWarner Losh struct ifnet *ifp; 178091c265b8SPyun YongHyeon uint32_t status; 1781d2155f2fSWarner Losh 1782d2155f2fSWarner Losh sc = arg; 1783d2155f2fSWarner Losh ifp = sc->sis_ifp; 1784d2155f2fSWarner Losh 1785d2155f2fSWarner Losh SIS_LOCK(sc); 1786d2155f2fSWarner Losh #ifdef DEVICE_POLLING 1787d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 1788d2155f2fSWarner Losh SIS_UNLOCK(sc); 1789d2155f2fSWarner Losh return; 1790d2155f2fSWarner Losh } 1791d2155f2fSWarner Losh #endif 1792d2155f2fSWarner Losh 1793d7b57e79SPyun YongHyeon /* Reading the ISR register clears all interrupts. */ 1794d7b57e79SPyun YongHyeon status = CSR_READ_4(sc, SIS_ISR); 1795d7b57e79SPyun YongHyeon if ((status & SIS_INTRS) == 0) { 1796d7b57e79SPyun YongHyeon /* Not ours. */ 1797d7b57e79SPyun YongHyeon SIS_UNLOCK(sc); 1798d7b57e79SPyun YongHyeon } 1799d7b57e79SPyun YongHyeon 1800d2155f2fSWarner Losh /* Disable interrupts. */ 1801d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 1802d2155f2fSWarner Losh 1803d7b57e79SPyun YongHyeon for (;(status & SIS_INTRS) != 0;) { 1804d2155f2fSWarner Losh if (status & 1805d2155f2fSWarner Losh (SIS_ISR_TX_DESC_OK | SIS_ISR_TX_ERR | 1806d2155f2fSWarner Losh SIS_ISR_TX_OK | SIS_ISR_TX_IDLE) ) 1807d2155f2fSWarner Losh sis_txeof(sc); 1808d2155f2fSWarner Losh 180953414a48SPyun YongHyeon if (status & (SIS_ISR_RX_DESC_OK | SIS_ISR_RX_OK | 181053414a48SPyun YongHyeon SIS_ISR_RX_ERR | SIS_ISR_RX_IDLE)) 1811d2155f2fSWarner Losh sis_rxeof(sc); 1812d2155f2fSWarner Losh 181353414a48SPyun YongHyeon if (status & SIS_ISR_RX_OFLOW) 181462592d91SRebecca Cran ifp->if_ierrors++; 1815d2155f2fSWarner Losh 1816d2155f2fSWarner Losh if (status & (SIS_ISR_RX_IDLE)) 1817d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 1818d2155f2fSWarner Losh 1819d2155f2fSWarner Losh if (status & SIS_ISR_SYSERR) { 1820d199ef7eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1821d2155f2fSWarner Losh sis_initl(sc); 1822d7b57e79SPyun YongHyeon SIS_UNLOCK(sc); 1823d7b57e79SPyun YongHyeon return; 1824d2155f2fSWarner Losh } 1825d7b57e79SPyun YongHyeon status = CSR_READ_4(sc, SIS_ISR); 1826d2155f2fSWarner Losh } 1827d2155f2fSWarner Losh 1828d2155f2fSWarner Losh /* Re-enable interrupts. */ 1829d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 1830d2155f2fSWarner Losh 1831d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1832d2155f2fSWarner Losh sis_startl(ifp); 1833d2155f2fSWarner Losh 1834d2155f2fSWarner Losh SIS_UNLOCK(sc); 1835d2155f2fSWarner Losh } 1836d2155f2fSWarner Losh 1837d2155f2fSWarner Losh /* 1838d2155f2fSWarner Losh * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 1839d2155f2fSWarner Losh * pointers to the fragment pointers. 1840d2155f2fSWarner Losh */ 1841d2155f2fSWarner Losh static int 1842a629f2b1SPyun YongHyeon sis_encap(struct sis_softc *sc, struct mbuf **m_head) 1843d2155f2fSWarner Losh { 1844d2155f2fSWarner Losh struct mbuf *m; 1845a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 1846a629f2b1SPyun YongHyeon struct sis_desc *f; 1847a629f2b1SPyun YongHyeon bus_dma_segment_t segs[SIS_MAXTXSEGS]; 1848a629f2b1SPyun YongHyeon bus_dmamap_t map; 1849a629f2b1SPyun YongHyeon int error, i, frag, nsegs, prod; 185094222398SPyun YongHyeon int padlen; 1851d2155f2fSWarner Losh 1852a629f2b1SPyun YongHyeon prod = sc->sis_tx_prod; 1853a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[prod]; 185494222398SPyun YongHyeon if ((sc->sis_flags & SIS_FLAG_MANUAL_PAD) != 0 && 185594222398SPyun YongHyeon (*m_head)->m_pkthdr.len < SIS_MIN_FRAMELEN) { 185694222398SPyun YongHyeon m = *m_head; 185794222398SPyun YongHyeon padlen = SIS_MIN_FRAMELEN - m->m_pkthdr.len; 185894222398SPyun YongHyeon if (M_WRITABLE(m) == 0) { 185994222398SPyun YongHyeon /* Get a writable copy. */ 186094222398SPyun YongHyeon m = m_dup(*m_head, M_DONTWAIT); 186194222398SPyun YongHyeon m_freem(*m_head); 186294222398SPyun YongHyeon if (m == NULL) { 186394222398SPyun YongHyeon *m_head = NULL; 186494222398SPyun YongHyeon return (ENOBUFS); 186594222398SPyun YongHyeon } 186694222398SPyun YongHyeon *m_head = m; 186794222398SPyun YongHyeon } 186894222398SPyun YongHyeon if (m->m_next != NULL || M_TRAILINGSPACE(m) < padlen) { 186994222398SPyun YongHyeon m = m_defrag(m, M_DONTWAIT); 187094222398SPyun YongHyeon if (m == NULL) { 187194222398SPyun YongHyeon m_freem(*m_head); 187294222398SPyun YongHyeon *m_head = NULL; 187394222398SPyun YongHyeon return (ENOBUFS); 187494222398SPyun YongHyeon } 187594222398SPyun YongHyeon } 187694222398SPyun YongHyeon /* 187794222398SPyun YongHyeon * Manually pad short frames, and zero the pad space 187894222398SPyun YongHyeon * to avoid leaking data. 187994222398SPyun YongHyeon */ 188094222398SPyun YongHyeon bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); 188194222398SPyun YongHyeon m->m_pkthdr.len += padlen; 188294222398SPyun YongHyeon m->m_len = m->m_pkthdr.len; 188394222398SPyun YongHyeon *m_head = m; 188494222398SPyun YongHyeon } 1885a629f2b1SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->sis_tx_tag, txd->tx_dmamap, 1886a629f2b1SPyun YongHyeon *m_head, segs, &nsegs, 0); 1887a629f2b1SPyun YongHyeon if (error == EFBIG) { 1888a629f2b1SPyun YongHyeon m = m_collapse(*m_head, M_DONTWAIT, SIS_MAXTXSEGS); 1889a629f2b1SPyun YongHyeon if (m == NULL) { 1890a629f2b1SPyun YongHyeon m_freem(*m_head); 1891a629f2b1SPyun YongHyeon *m_head = NULL; 1892d2155f2fSWarner Losh return (ENOBUFS); 1893a629f2b1SPyun YongHyeon } 1894d2155f2fSWarner Losh *m_head = m; 1895a629f2b1SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->sis_tx_tag, txd->tx_dmamap, 1896a629f2b1SPyun YongHyeon *m_head, segs, &nsegs, 0); 1897a629f2b1SPyun YongHyeon if (error != 0) { 1898a629f2b1SPyun YongHyeon m_freem(*m_head); 1899a629f2b1SPyun YongHyeon *m_head = NULL; 1900a629f2b1SPyun YongHyeon return (error); 1901a629f2b1SPyun YongHyeon } 1902a629f2b1SPyun YongHyeon } else if (error != 0) 1903a629f2b1SPyun YongHyeon return (error); 1904a629f2b1SPyun YongHyeon 1905a629f2b1SPyun YongHyeon /* Check for descriptor overruns. */ 1906a629f2b1SPyun YongHyeon if (sc->sis_tx_cnt + nsegs > SIS_TX_LIST_CNT - 1) { 1907a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_tx_tag, txd->tx_dmamap); 1908a629f2b1SPyun YongHyeon return (ENOBUFS); 1909d2155f2fSWarner Losh } 1910d2155f2fSWarner Losh 1911a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_tag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); 1912d2155f2fSWarner Losh 1913a629f2b1SPyun YongHyeon frag = prod; 1914a629f2b1SPyun YongHyeon for (i = 0; i < nsegs; i++) { 1915a629f2b1SPyun YongHyeon f = &sc->sis_tx_list[prod]; 1916a629f2b1SPyun YongHyeon if (i == 0) 1917a629f2b1SPyun YongHyeon f->sis_cmdsts = htole32(segs[i].ds_len | 1918a629f2b1SPyun YongHyeon SIS_CMDSTS_MORE); 1919a629f2b1SPyun YongHyeon else 1920a629f2b1SPyun YongHyeon f->sis_cmdsts = htole32(segs[i].ds_len | 1921a629f2b1SPyun YongHyeon SIS_CMDSTS_OWN | SIS_CMDSTS_MORE); 1922a629f2b1SPyun YongHyeon f->sis_ptr = htole32(SIS_ADDR_LO(segs[i].ds_addr)); 1923a629f2b1SPyun YongHyeon SIS_INC(prod, SIS_TX_LIST_CNT); 1924a629f2b1SPyun YongHyeon sc->sis_tx_cnt++; 1925a629f2b1SPyun YongHyeon } 1926a629f2b1SPyun YongHyeon 1927a629f2b1SPyun YongHyeon /* Update producer index. */ 1928a629f2b1SPyun YongHyeon sc->sis_tx_prod = prod; 1929a629f2b1SPyun YongHyeon 1930a629f2b1SPyun YongHyeon /* Remove MORE flag on the last descriptor. */ 1931a629f2b1SPyun YongHyeon prod = (prod - 1) & (SIS_TX_LIST_CNT - 1); 1932a629f2b1SPyun YongHyeon f = &sc->sis_tx_list[prod]; 1933a629f2b1SPyun YongHyeon f->sis_cmdsts &= ~htole32(SIS_CMDSTS_MORE); 1934a629f2b1SPyun YongHyeon 1935a629f2b1SPyun YongHyeon /* Lastly transfer ownership of packet to the controller. */ 1936d2155f2fSWarner Losh f = &sc->sis_tx_list[frag]; 1937a629f2b1SPyun YongHyeon f->sis_cmdsts |= htole32(SIS_CMDSTS_OWN); 1938d2155f2fSWarner Losh 1939a629f2b1SPyun YongHyeon /* Swap the last and the first dmamaps. */ 1940a629f2b1SPyun YongHyeon map = txd->tx_dmamap; 19418c6cd863SPyun YongHyeon txd->tx_dmamap = sc->sis_txdesc[prod].tx_dmamap; 19428c6cd863SPyun YongHyeon sc->sis_txdesc[prod].tx_dmamap = map; 1943a629f2b1SPyun YongHyeon txd->tx_m = *m_head; 1944d2155f2fSWarner Losh 1945d2155f2fSWarner Losh return (0); 1946d2155f2fSWarner Losh } 1947d2155f2fSWarner Losh 1948d2155f2fSWarner Losh static void 1949d2155f2fSWarner Losh sis_start(struct ifnet *ifp) 1950d2155f2fSWarner Losh { 1951d2155f2fSWarner Losh struct sis_softc *sc; 1952d2155f2fSWarner Losh 1953d2155f2fSWarner Losh sc = ifp->if_softc; 1954d2155f2fSWarner Losh SIS_LOCK(sc); 1955d2155f2fSWarner Losh sis_startl(ifp); 1956d2155f2fSWarner Losh SIS_UNLOCK(sc); 1957d2155f2fSWarner Losh } 1958d2155f2fSWarner Losh 1959d2155f2fSWarner Losh static void 1960d2155f2fSWarner Losh sis_startl(struct ifnet *ifp) 1961d2155f2fSWarner Losh { 1962d2155f2fSWarner Losh struct sis_softc *sc; 1963a629f2b1SPyun YongHyeon struct mbuf *m_head; 1964a629f2b1SPyun YongHyeon int queued; 1965d2155f2fSWarner Losh 1966d2155f2fSWarner Losh sc = ifp->if_softc; 1967d2155f2fSWarner Losh 1968d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 1969d2155f2fSWarner Losh 1970a629f2b1SPyun YongHyeon if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 197194222398SPyun YongHyeon IFF_DRV_RUNNING || (sc->sis_flags & SIS_FLAG_LINK) == 0) 1972d2155f2fSWarner Losh return; 1973d2155f2fSWarner Losh 1974a629f2b1SPyun YongHyeon for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && 1975a629f2b1SPyun YongHyeon sc->sis_tx_cnt < SIS_TX_LIST_CNT - 4;) { 1976d2155f2fSWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 1977d2155f2fSWarner Losh if (m_head == NULL) 1978d2155f2fSWarner Losh break; 1979d2155f2fSWarner Losh 1980a629f2b1SPyun YongHyeon if (sis_encap(sc, &m_head) != 0) { 1981a629f2b1SPyun YongHyeon if (m_head == NULL) 1982a629f2b1SPyun YongHyeon break; 1983d2155f2fSWarner Losh IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 1984d2155f2fSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1985d2155f2fSWarner Losh break; 1986d2155f2fSWarner Losh } 1987d2155f2fSWarner Losh 1988d2155f2fSWarner Losh queued++; 1989d2155f2fSWarner Losh 1990d2155f2fSWarner Losh /* 1991d2155f2fSWarner Losh * If there's a BPF listener, bounce a copy of this frame 1992d2155f2fSWarner Losh * to him. 1993d2155f2fSWarner Losh */ 1994d2155f2fSWarner Losh BPF_MTAP(ifp, m_head); 1995d2155f2fSWarner Losh } 1996d2155f2fSWarner Losh 1997d2155f2fSWarner Losh if (queued) { 1998d2155f2fSWarner Losh /* Transmit */ 1999a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_list_tag, sc->sis_tx_list_map, 2000a629f2b1SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2001d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); 2002d2155f2fSWarner Losh 2003d2155f2fSWarner Losh /* 2004d2155f2fSWarner Losh * Set a timeout in case the chip goes out to lunch. 2005d2155f2fSWarner Losh */ 2006d2155f2fSWarner Losh sc->sis_watchdog_timer = 5; 2007d2155f2fSWarner Losh } 2008d2155f2fSWarner Losh } 2009d2155f2fSWarner Losh 2010d2155f2fSWarner Losh static void 2011d2155f2fSWarner Losh sis_init(void *xsc) 2012d2155f2fSWarner Losh { 2013d2155f2fSWarner Losh struct sis_softc *sc = xsc; 2014d2155f2fSWarner Losh 2015d2155f2fSWarner Losh SIS_LOCK(sc); 2016d2155f2fSWarner Losh sis_initl(sc); 2017d2155f2fSWarner Losh SIS_UNLOCK(sc); 2018d2155f2fSWarner Losh } 2019d2155f2fSWarner Losh 2020d2155f2fSWarner Losh static void 2021d2155f2fSWarner Losh sis_initl(struct sis_softc *sc) 2022d2155f2fSWarner Losh { 2023d2155f2fSWarner Losh struct ifnet *ifp = sc->sis_ifp; 2024d2155f2fSWarner Losh struct mii_data *mii; 202574e8a323SPyun YongHyeon uint8_t *eaddr; 2026d2155f2fSWarner Losh 2027d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 2028d2155f2fSWarner Losh 2029d199ef7eSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 2030d199ef7eSPyun YongHyeon return; 2031d199ef7eSPyun YongHyeon 2032d2155f2fSWarner Losh /* 2033d2155f2fSWarner Losh * Cancel pending I/O and free all RX/TX buffers. 2034d2155f2fSWarner Losh */ 2035d2155f2fSWarner Losh sis_stop(sc); 20367723fa2eSPyun YongHyeon /* 20377723fa2eSPyun YongHyeon * Reset the chip to a known state. 20387723fa2eSPyun YongHyeon */ 20397723fa2eSPyun YongHyeon sis_reset(sc); 2040d2155f2fSWarner Losh #ifdef notyet 2041d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr >= NS_SRR_16A) { 2042d2155f2fSWarner Losh /* 2043d2155f2fSWarner Losh * Configure 400usec of interrupt holdoff. This is based 2044d2155f2fSWarner Losh * on emperical tests on a Soekris 4801. 2045d2155f2fSWarner Losh */ 2046d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_IHR, 0x100 | 4); 2047d2155f2fSWarner Losh } 2048d2155f2fSWarner Losh #endif 2049d2155f2fSWarner Losh 2050d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2051d2155f2fSWarner Losh 2052d2155f2fSWarner Losh /* Set MAC address */ 205374e8a323SPyun YongHyeon eaddr = IF_LLADDR(sc->sis_ifp); 2054d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 2055d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); 205674e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[0] | eaddr[1] << 8); 2057d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); 205874e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[2] | eaddr[3] << 8); 2059d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); 206074e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[4] | eaddr[5] << 8); 2061d2155f2fSWarner Losh } else { 2062d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 206374e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[0] | eaddr[1] << 8); 2064d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); 206574e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[2] | eaddr[3] << 8); 2066d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 206774e8a323SPyun YongHyeon CSR_WRITE_4(sc, SIS_RXFILT_DATA, eaddr[4] | eaddr[5] << 8); 2068d2155f2fSWarner Losh } 2069d2155f2fSWarner Losh 2070d2155f2fSWarner Losh /* Init circular TX/RX lists. */ 2071d2155f2fSWarner Losh if (sis_ring_init(sc) != 0) { 2072d2155f2fSWarner Losh device_printf(sc->sis_dev, 2073d2155f2fSWarner Losh "initialization failed: no memory for rx buffers\n"); 2074d2155f2fSWarner Losh sis_stop(sc); 2075d2155f2fSWarner Losh return; 2076d2155f2fSWarner Losh } 2077d2155f2fSWarner Losh 207894222398SPyun YongHyeon if (sc->sis_type == SIS_TYPE_83815 || sc->sis_type == SIS_TYPE_83816) { 207994222398SPyun YongHyeon if (sc->sis_manual_pad != 0) 208094222398SPyun YongHyeon sc->sis_flags |= SIS_FLAG_MANUAL_PAD; 208194222398SPyun YongHyeon else 208294222398SPyun YongHyeon sc->sis_flags &= ~SIS_FLAG_MANUAL_PAD; 208394222398SPyun YongHyeon } 208494222398SPyun YongHyeon 2085d2155f2fSWarner Losh /* 2086d2155f2fSWarner Losh * Short Cable Receive Errors (MP21.E) 2087d2155f2fSWarner Losh * also: Page 78 of the DP83815 data sheet (september 2002 version) 2088d2155f2fSWarner Losh * recommends the following register settings "for optimum 2089d2155f2fSWarner Losh * performance." for rev 15C. Set this also for 15D parts as 2090d2155f2fSWarner Losh * they require it in practice. 2091d2155f2fSWarner Losh */ 2092d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr <= NS_SRR_15D) { 2093d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); 2094d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_CR, 0x189C); 2095d2155f2fSWarner Losh /* set val for c2 */ 2096d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000); 2097d2155f2fSWarner Losh /* load/kill c2 */ 2098d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040); 2099d2155f2fSWarner Losh /* rais SD off, from 4 to c */ 2100d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C); 2101d2155f2fSWarner Losh CSR_WRITE_4(sc, NS_PHY_PAGE, 0); 2102d2155f2fSWarner Losh } 2103d2155f2fSWarner Losh 2104d2155f2fSWarner Losh /* 2105d2155f2fSWarner Losh * For the NatSemi chip, we have to explicitly enable the 2106d2155f2fSWarner Losh * reception of ARP frames, as well as turn on the 'perfect 2107d2155f2fSWarner Losh * match' filter where we store the station address, otherwise 2108d2155f2fSWarner Losh * we won't receive unicasts meant for this host. 2109d2155f2fSWarner Losh */ 2110d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) { 2111d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); 2112d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); 2113d2155f2fSWarner Losh } 2114d2155f2fSWarner Losh 2115d2155f2fSWarner Losh /* If we want promiscuous mode, set the allframes bit. */ 2116d2155f2fSWarner Losh if (ifp->if_flags & IFF_PROMISC) { 2117d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 2118d2155f2fSWarner Losh } else { 2119d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 2120d2155f2fSWarner Losh } 2121d2155f2fSWarner Losh 2122d2155f2fSWarner Losh /* 2123d2155f2fSWarner Losh * Set the capture broadcast bit to capture broadcast frames. 2124d2155f2fSWarner Losh */ 2125d2155f2fSWarner Losh if (ifp->if_flags & IFF_BROADCAST) { 2126d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 2127d2155f2fSWarner Losh } else { 2128d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 2129d2155f2fSWarner Losh } 2130d2155f2fSWarner Losh 2131d2155f2fSWarner Losh /* 2132d2155f2fSWarner Losh * Load the multicast filter. 2133d2155f2fSWarner Losh */ 2134d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 2135d2155f2fSWarner Losh sis_setmulti_ns(sc); 2136d2155f2fSWarner Losh else 2137d2155f2fSWarner Losh sis_setmulti_sis(sc); 2138d2155f2fSWarner Losh 2139d2155f2fSWarner Losh /* Turn the receive filter on */ 2140d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); 2141d2155f2fSWarner Losh 2142d2155f2fSWarner Losh /* 2143d2155f2fSWarner Losh * Load the address of the RX and TX lists. 2144d2155f2fSWarner Losh */ 2145a629f2b1SPyun YongHyeon CSR_WRITE_4(sc, SIS_RX_LISTPTR, SIS_ADDR_LO(sc->sis_rx_paddr)); 2146a629f2b1SPyun YongHyeon CSR_WRITE_4(sc, SIS_TX_LISTPTR, SIS_ADDR_LO(sc->sis_tx_paddr)); 2147d2155f2fSWarner Losh 2148d2155f2fSWarner Losh /* SIS_CFG_EDB_MASTER_EN indicates the EDB bus is used instead of 2149d2155f2fSWarner Losh * the PCI bus. When this bit is set, the Max DMA Burst Size 2150d2155f2fSWarner Losh * for TX/RX DMA should be no larger than 16 double words. 2151d2155f2fSWarner Losh */ 2152d2155f2fSWarner Losh if (CSR_READ_4(sc, SIS_CFG) & SIS_CFG_EDB_MASTER_EN) { 2153d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG64); 2154d2155f2fSWarner Losh } else { 2155d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG256); 2156d2155f2fSWarner Losh } 2157d2155f2fSWarner Losh 2158d2155f2fSWarner Losh /* Accept Long Packets for VLAN support */ 2159d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_JABBER); 2160d2155f2fSWarner Losh 2161d7b57e79SPyun YongHyeon /* 2162d7b57e79SPyun YongHyeon * Assume 100Mbps link, actual MAC configuration is done 2163d7b57e79SPyun YongHyeon * after getting a valid link. 2164d7b57e79SPyun YongHyeon */ 2165d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); 2166d2155f2fSWarner Losh 2167d2155f2fSWarner Losh /* 2168d2155f2fSWarner Losh * Enable interrupts. 2169d2155f2fSWarner Losh */ 2170d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); 2171d2155f2fSWarner Losh #ifdef DEVICE_POLLING 2172d2155f2fSWarner Losh /* 2173d2155f2fSWarner Losh * ... only enable interrupts if we are not polling, make sure 2174d2155f2fSWarner Losh * they are off otherwise. 2175d2155f2fSWarner Losh */ 2176d2155f2fSWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 2177d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 2178d2155f2fSWarner Losh else 2179d2155f2fSWarner Losh #endif 2180d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 2181d2155f2fSWarner Losh 2182d7b57e79SPyun YongHyeon /* Clear MAC disable. */ 2183d2155f2fSWarner Losh SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE | SIS_CSR_RX_DISABLE); 2184d2155f2fSWarner Losh 218594222398SPyun YongHyeon sc->sis_flags &= ~SIS_FLAG_LINK; 2186d2155f2fSWarner Losh mii_mediachg(mii); 2187d2155f2fSWarner Losh 2188d2155f2fSWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 2189d2155f2fSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2190d2155f2fSWarner Losh 2191d2155f2fSWarner Losh callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); 2192d2155f2fSWarner Losh } 2193d2155f2fSWarner Losh 2194d2155f2fSWarner Losh /* 2195d2155f2fSWarner Losh * Set media options. 2196d2155f2fSWarner Losh */ 2197d2155f2fSWarner Losh static int 2198d2155f2fSWarner Losh sis_ifmedia_upd(struct ifnet *ifp) 2199d2155f2fSWarner Losh { 2200d2155f2fSWarner Losh struct sis_softc *sc; 2201d2155f2fSWarner Losh struct mii_data *mii; 2202fc58ee15SPyun YongHyeon int error; 2203d2155f2fSWarner Losh 2204d2155f2fSWarner Losh sc = ifp->if_softc; 2205d2155f2fSWarner Losh 2206d2155f2fSWarner Losh SIS_LOCK(sc); 2207d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2208d2155f2fSWarner Losh if (mii->mii_instance) { 2209d2155f2fSWarner Losh struct mii_softc *miisc; 2210d2155f2fSWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 2211d2155f2fSWarner Losh mii_phy_reset(miisc); 2212d2155f2fSWarner Losh } 2213fc58ee15SPyun YongHyeon error = mii_mediachg(mii); 2214d2155f2fSWarner Losh SIS_UNLOCK(sc); 2215d2155f2fSWarner Losh 2216fc58ee15SPyun YongHyeon return (error); 2217d2155f2fSWarner Losh } 2218d2155f2fSWarner Losh 2219d2155f2fSWarner Losh /* 2220d2155f2fSWarner Losh * Report current media status. 2221d2155f2fSWarner Losh */ 2222d2155f2fSWarner Losh static void 2223d2155f2fSWarner Losh sis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 2224d2155f2fSWarner Losh { 2225d2155f2fSWarner Losh struct sis_softc *sc; 2226d2155f2fSWarner Losh struct mii_data *mii; 2227d2155f2fSWarner Losh 2228d2155f2fSWarner Losh sc = ifp->if_softc; 2229d2155f2fSWarner Losh 2230d2155f2fSWarner Losh SIS_LOCK(sc); 2231d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2232d2155f2fSWarner Losh mii_pollstat(mii); 2233d2155f2fSWarner Losh SIS_UNLOCK(sc); 2234d2155f2fSWarner Losh ifmr->ifm_active = mii->mii_media_active; 2235d2155f2fSWarner Losh ifmr->ifm_status = mii->mii_media_status; 2236d2155f2fSWarner Losh } 2237d2155f2fSWarner Losh 2238d2155f2fSWarner Losh static int 2239d2155f2fSWarner Losh sis_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2240d2155f2fSWarner Losh { 2241d2155f2fSWarner Losh struct sis_softc *sc = ifp->if_softc; 2242d2155f2fSWarner Losh struct ifreq *ifr = (struct ifreq *) data; 2243d2155f2fSWarner Losh struct mii_data *mii; 2244*0af3989bSPyun YongHyeon int error = 0, mask; 2245d2155f2fSWarner Losh 2246d2155f2fSWarner Losh switch (command) { 2247d2155f2fSWarner Losh case SIOCSIFFLAGS: 2248d2155f2fSWarner Losh SIS_LOCK(sc); 2249d2155f2fSWarner Losh if (ifp->if_flags & IFF_UP) { 2250ae9e8d49SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 2251ae9e8d49SPyun YongHyeon ((ifp->if_flags ^ sc->sis_if_flags) & 2252ae9e8d49SPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 2253ae9e8d49SPyun YongHyeon if (sc->sis_type == SIS_TYPE_83815) 2254ae9e8d49SPyun YongHyeon sis_setmulti_ns(sc); 2255ae9e8d49SPyun YongHyeon else 2256ae9e8d49SPyun YongHyeon sis_setmulti_sis(sc); 2257ae9e8d49SPyun YongHyeon } else 2258d2155f2fSWarner Losh sis_initl(sc); 2259d2155f2fSWarner Losh } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2260d2155f2fSWarner Losh sis_stop(sc); 2261d2155f2fSWarner Losh } 2262ae9e8d49SPyun YongHyeon sc->sis_if_flags = ifp->if_flags; 2263d2155f2fSWarner Losh SIS_UNLOCK(sc); 2264d2155f2fSWarner Losh error = 0; 2265d2155f2fSWarner Losh break; 2266d2155f2fSWarner Losh case SIOCADDMULTI: 2267d2155f2fSWarner Losh case SIOCDELMULTI: 2268d2155f2fSWarner Losh SIS_LOCK(sc); 2269d2155f2fSWarner Losh if (sc->sis_type == SIS_TYPE_83815) 2270d2155f2fSWarner Losh sis_setmulti_ns(sc); 2271d2155f2fSWarner Losh else 2272d2155f2fSWarner Losh sis_setmulti_sis(sc); 2273d2155f2fSWarner Losh SIS_UNLOCK(sc); 2274d2155f2fSWarner Losh break; 2275d2155f2fSWarner Losh case SIOCGIFMEDIA: 2276d2155f2fSWarner Losh case SIOCSIFMEDIA: 2277d2155f2fSWarner Losh mii = device_get_softc(sc->sis_miibus); 2278d2155f2fSWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 2279d2155f2fSWarner Losh break; 2280d2155f2fSWarner Losh case SIOCSIFCAP: 2281d2155f2fSWarner Losh SIS_LOCK(sc); 2282*0af3989bSPyun YongHyeon mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2283*0af3989bSPyun YongHyeon #ifdef DEVICE_POLLING 2284*0af3989bSPyun YongHyeon if ((mask & IFCAP_POLLING) != 0 && 2285*0af3989bSPyun YongHyeon (IFCAP_POLLING & ifp->if_capabilities) != 0) { 2286*0af3989bSPyun YongHyeon ifp->if_capenable ^= IFCAP_POLLING; 2287*0af3989bSPyun YongHyeon if ((IFCAP_POLLING & ifp->if_capenable) != 0) { 2288*0af3989bSPyun YongHyeon error = ether_poll_register(sis_poll, ifp); 2289*0af3989bSPyun YongHyeon if (error != 0) { 2290d2155f2fSWarner Losh SIS_UNLOCK(sc); 2291*0af3989bSPyun YongHyeon break; 2292d2155f2fSWarner Losh } 2293*0af3989bSPyun YongHyeon /* Disable interrupts. */ 2294*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, SIS_IER, 0); 2295*0af3989bSPyun YongHyeon } else { 2296d2155f2fSWarner Losh error = ether_poll_deregister(ifp); 2297d2155f2fSWarner Losh /* Enable interrupts. */ 2298d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 1); 2299*0af3989bSPyun YongHyeon } 2300d2155f2fSWarner Losh } 2301d2155f2fSWarner Losh #endif /* DEVICE_POLLING */ 2302*0af3989bSPyun YongHyeon if ((mask & IFCAP_WOL) != 0 && 2303*0af3989bSPyun YongHyeon (ifp->if_capabilities & IFCAP_WOL) != 0) { 2304*0af3989bSPyun YongHyeon if ((mask & IFCAP_WOL_UCAST) != 0) 2305*0af3989bSPyun YongHyeon ifp->if_capenable ^= IFCAP_WOL_UCAST; 2306*0af3989bSPyun YongHyeon if ((mask & IFCAP_WOL_MCAST) != 0) 2307*0af3989bSPyun YongHyeon ifp->if_capenable ^= IFCAP_WOL_MCAST; 2308*0af3989bSPyun YongHyeon if ((mask & IFCAP_WOL_MAGIC) != 0) 2309*0af3989bSPyun YongHyeon ifp->if_capenable ^= IFCAP_WOL_MAGIC; 2310*0af3989bSPyun YongHyeon } 2311*0af3989bSPyun YongHyeon SIS_UNLOCK(sc); 2312d2155f2fSWarner Losh break; 2313d2155f2fSWarner Losh default: 2314d2155f2fSWarner Losh error = ether_ioctl(ifp, command, data); 2315d2155f2fSWarner Losh break; 2316d2155f2fSWarner Losh } 2317d2155f2fSWarner Losh 2318d2155f2fSWarner Losh return (error); 2319d2155f2fSWarner Losh } 2320d2155f2fSWarner Losh 2321d2155f2fSWarner Losh static void 2322d2155f2fSWarner Losh sis_watchdog(struct sis_softc *sc) 2323d2155f2fSWarner Losh { 2324d2155f2fSWarner Losh 2325d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 2326d2155f2fSWarner Losh 2327d2155f2fSWarner Losh if (sc->sis_watchdog_timer == 0 || --sc->sis_watchdog_timer >0) 2328d2155f2fSWarner Losh return; 2329d2155f2fSWarner Losh 2330d2155f2fSWarner Losh device_printf(sc->sis_dev, "watchdog timeout\n"); 2331d2155f2fSWarner Losh sc->sis_ifp->if_oerrors++; 2332d2155f2fSWarner Losh 23337723fa2eSPyun YongHyeon sc->sis_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2334d2155f2fSWarner Losh sis_initl(sc); 2335d2155f2fSWarner Losh 2336d2155f2fSWarner Losh if (!IFQ_DRV_IS_EMPTY(&sc->sis_ifp->if_snd)) 2337d2155f2fSWarner Losh sis_startl(sc->sis_ifp); 2338d2155f2fSWarner Losh } 2339d2155f2fSWarner Losh 2340d2155f2fSWarner Losh /* 2341d2155f2fSWarner Losh * Stop the adapter and free any mbufs allocated to the 2342d2155f2fSWarner Losh * RX and TX lists. 2343d2155f2fSWarner Losh */ 2344d2155f2fSWarner Losh static void 2345d2155f2fSWarner Losh sis_stop(struct sis_softc *sc) 2346d2155f2fSWarner Losh { 2347d2155f2fSWarner Losh struct ifnet *ifp; 2348a629f2b1SPyun YongHyeon struct sis_rxdesc *rxd; 2349a629f2b1SPyun YongHyeon struct sis_txdesc *txd; 2350a629f2b1SPyun YongHyeon int i; 2351d2155f2fSWarner Losh 2352d2155f2fSWarner Losh SIS_LOCK_ASSERT(sc); 2353d7b57e79SPyun YongHyeon 2354d2155f2fSWarner Losh ifp = sc->sis_ifp; 2355d2155f2fSWarner Losh sc->sis_watchdog_timer = 0; 2356d2155f2fSWarner Losh 2357d2155f2fSWarner Losh callout_stop(&sc->sis_stat_ch); 2358d2155f2fSWarner Losh 2359d2155f2fSWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2360d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IER, 0); 2361d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_IMR, 0); 2362d2155f2fSWarner Losh CSR_READ_4(sc, SIS_ISR); /* clear any interrupts already pending */ 2363d2155f2fSWarner Losh SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 2364d2155f2fSWarner Losh DELAY(1000); 2365d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); 2366d2155f2fSWarner Losh CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 2367d2155f2fSWarner Losh 236894222398SPyun YongHyeon sc->sis_flags &= ~SIS_FLAG_LINK; 2369d2155f2fSWarner Losh 2370d2155f2fSWarner Losh /* 2371d2155f2fSWarner Losh * Free data in the RX lists. 2372d2155f2fSWarner Losh */ 2373a629f2b1SPyun YongHyeon for (i = 0; i < SIS_RX_LIST_CNT; i++) { 2374a629f2b1SPyun YongHyeon rxd = &sc->sis_rxdesc[i]; 2375a629f2b1SPyun YongHyeon if (rxd->rx_m != NULL) { 2376a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_rx_tag, rxd->rx_dmamap, 2377a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTREAD); 2378a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_rx_tag, rxd->rx_dmamap); 2379a629f2b1SPyun YongHyeon m_freem(rxd->rx_m); 2380a629f2b1SPyun YongHyeon rxd->rx_m = NULL; 2381d2155f2fSWarner Losh } 2382a629f2b1SPyun YongHyeon } 2383d2155f2fSWarner Losh 2384d2155f2fSWarner Losh /* 2385d2155f2fSWarner Losh * Free the TX list buffers. 2386d2155f2fSWarner Losh */ 2387a629f2b1SPyun YongHyeon for (i = 0; i < SIS_TX_LIST_CNT; i++) { 2388a629f2b1SPyun YongHyeon txd = &sc->sis_txdesc[i]; 2389a629f2b1SPyun YongHyeon if (txd->tx_m != NULL) { 2390a629f2b1SPyun YongHyeon bus_dmamap_sync(sc->sis_tx_tag, txd->tx_dmamap, 2391a629f2b1SPyun YongHyeon BUS_DMASYNC_POSTWRITE); 2392a629f2b1SPyun YongHyeon bus_dmamap_unload(sc->sis_tx_tag, txd->tx_dmamap); 2393a629f2b1SPyun YongHyeon m_freem(txd->tx_m); 2394a629f2b1SPyun YongHyeon txd->tx_m = NULL; 2395d2155f2fSWarner Losh } 2396a629f2b1SPyun YongHyeon } 2397d2155f2fSWarner Losh } 2398d2155f2fSWarner Losh 2399d2155f2fSWarner Losh /* 2400d2155f2fSWarner Losh * Stop all chip I/O so that the kernel's probe routines don't 2401d2155f2fSWarner Losh * get confused by errant DMAs when rebooting. 2402d2155f2fSWarner Losh */ 2403e436c382SWarner Losh static int 2404d2155f2fSWarner Losh sis_shutdown(device_t dev) 2405d2155f2fSWarner Losh { 2406d2155f2fSWarner Losh 2407*0af3989bSPyun YongHyeon return (sis_suspend(dev)); 2408d2155f2fSWarner Losh } 2409d2155f2fSWarner Losh 24107968da57SPyun YongHyeon static int 24117968da57SPyun YongHyeon sis_suspend(device_t dev) 24127968da57SPyun YongHyeon { 24137968da57SPyun YongHyeon struct sis_softc *sc; 24147968da57SPyun YongHyeon 24157968da57SPyun YongHyeon sc = device_get_softc(dev); 24167968da57SPyun YongHyeon SIS_LOCK(sc); 24177968da57SPyun YongHyeon sis_stop(sc); 2418*0af3989bSPyun YongHyeon sis_wol(sc); 24197968da57SPyun YongHyeon SIS_UNLOCK(sc); 24207968da57SPyun YongHyeon return (0); 24217968da57SPyun YongHyeon } 24227968da57SPyun YongHyeon 24237968da57SPyun YongHyeon static int 24247968da57SPyun YongHyeon sis_resume(device_t dev) 24257968da57SPyun YongHyeon { 24267968da57SPyun YongHyeon struct sis_softc *sc; 24277968da57SPyun YongHyeon struct ifnet *ifp; 24287968da57SPyun YongHyeon 24297968da57SPyun YongHyeon sc = device_get_softc(dev); 24307968da57SPyun YongHyeon SIS_LOCK(sc); 24317968da57SPyun YongHyeon ifp = sc->sis_ifp; 24327968da57SPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 24337968da57SPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 24347968da57SPyun YongHyeon sis_initl(sc); 24357968da57SPyun YongHyeon } 24367968da57SPyun YongHyeon SIS_UNLOCK(sc); 24377968da57SPyun YongHyeon return (0); 24387968da57SPyun YongHyeon } 24397968da57SPyun YongHyeon 244094222398SPyun YongHyeon static void 2441*0af3989bSPyun YongHyeon sis_wol(struct sis_softc *sc) 2442*0af3989bSPyun YongHyeon { 2443*0af3989bSPyun YongHyeon struct ifnet *ifp; 2444*0af3989bSPyun YongHyeon uint32_t val; 2445*0af3989bSPyun YongHyeon uint16_t pmstat; 2446*0af3989bSPyun YongHyeon int pmc; 2447*0af3989bSPyun YongHyeon 2448*0af3989bSPyun YongHyeon ifp = sc->sis_ifp; 2449*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL) == 0) 2450*0af3989bSPyun YongHyeon return; 2451*0af3989bSPyun YongHyeon 2452*0af3989bSPyun YongHyeon if (sc->sis_type == SIS_TYPE_83815) { 2453*0af3989bSPyun YongHyeon /* Reset RXDP. */ 2454*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 2455*0af3989bSPyun YongHyeon 2456*0af3989bSPyun YongHyeon /* Configure WOL events. */ 2457*0af3989bSPyun YongHyeon CSR_READ_4(sc, NS_WCSR); 2458*0af3989bSPyun YongHyeon val = 0; 2459*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0) 2460*0af3989bSPyun YongHyeon val |= NS_WCSR_WAKE_UCAST; 2461*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) 2462*0af3989bSPyun YongHyeon val |= NS_WCSR_WAKE_MCAST; 2463*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2464*0af3989bSPyun YongHyeon val |= NS_WCSR_WAKE_MAGIC; 2465*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, NS_WCSR, val); 2466*0af3989bSPyun YongHyeon /* Enable PME and clear PMESTS. */ 2467*0af3989bSPyun YongHyeon val = CSR_READ_4(sc, NS_CLKRUN); 2468*0af3989bSPyun YongHyeon val |= NS_CLKRUN_PMEENB | NS_CLKRUN_PMESTS; 2469*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, NS_CLKRUN, val); 2470*0af3989bSPyun YongHyeon /* Enable silent RX mode. */ 2471*0af3989bSPyun YongHyeon SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 2472*0af3989bSPyun YongHyeon } else { 2473*0af3989bSPyun YongHyeon if (pci_find_extcap(sc->sis_dev, PCIY_PMG, &pmc) != 0) 2474*0af3989bSPyun YongHyeon return; 2475*0af3989bSPyun YongHyeon val = 0; 2476*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2477*0af3989bSPyun YongHyeon val |= SIS_PWRMAN_WOL_MAGIC; 2478*0af3989bSPyun YongHyeon CSR_WRITE_4(sc, SIS_PWRMAN_CTL, val); 2479*0af3989bSPyun YongHyeon /* Request PME. */ 2480*0af3989bSPyun YongHyeon pmstat = pci_read_config(sc->sis_dev, 2481*0af3989bSPyun YongHyeon pmc + PCIR_POWER_STATUS, 2); 2482*0af3989bSPyun YongHyeon pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); 2483*0af3989bSPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2484*0af3989bSPyun YongHyeon pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; 2485*0af3989bSPyun YongHyeon pci_write_config(sc->sis_dev, 2486*0af3989bSPyun YongHyeon pmc + PCIR_POWER_STATUS, pmstat, 2); 2487*0af3989bSPyun YongHyeon } 2488*0af3989bSPyun YongHyeon } 2489*0af3989bSPyun YongHyeon 2490*0af3989bSPyun YongHyeon static void 249194222398SPyun YongHyeon sis_add_sysctls(struct sis_softc *sc) 249294222398SPyun YongHyeon { 249394222398SPyun YongHyeon struct sysctl_ctx_list *ctx; 249494222398SPyun YongHyeon struct sysctl_oid_list *children; 249594222398SPyun YongHyeon char tn[32]; 249694222398SPyun YongHyeon int unit; 249794222398SPyun YongHyeon 249894222398SPyun YongHyeon ctx = device_get_sysctl_ctx(sc->sis_dev); 249994222398SPyun YongHyeon children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sis_dev)); 250094222398SPyun YongHyeon 250194222398SPyun YongHyeon unit = device_get_unit(sc->sis_dev); 250294222398SPyun YongHyeon /* 250394222398SPyun YongHyeon * Unlike most other controllers, NS DP83815/DP83816 controllers 250494222398SPyun YongHyeon * seem to pad with 0xFF when it encounter short frames. According 250594222398SPyun YongHyeon * to RFC 1042 the pad bytes should be 0x00. Turning this tunable 250694222398SPyun YongHyeon * on will have driver pad manully but it's disabled by default 250794222398SPyun YongHyeon * because it will consume extra CPU cycles for short frames. 250894222398SPyun YongHyeon */ 250994222398SPyun YongHyeon sc->sis_manual_pad = 0; 251094222398SPyun YongHyeon snprintf(tn, sizeof(tn), "dev.sis.%d.manual_pad", unit); 251194222398SPyun YongHyeon TUNABLE_INT_FETCH(tn, &sc->sis_manual_pad); 251294222398SPyun YongHyeon SYSCTL_ADD_INT(ctx, children, OID_AUTO, "manual_pad", 251394222398SPyun YongHyeon CTLFLAG_RW, &sc->sis_manual_pad, 0, "Manually pad short frames"); 251494222398SPyun YongHyeon } 251594222398SPyun YongHyeon 2516d2155f2fSWarner Losh static device_method_t sis_methods[] = { 2517d2155f2fSWarner Losh /* Device interface */ 2518d2155f2fSWarner Losh DEVMETHOD(device_probe, sis_probe), 2519d2155f2fSWarner Losh DEVMETHOD(device_attach, sis_attach), 2520d2155f2fSWarner Losh DEVMETHOD(device_detach, sis_detach), 2521d2155f2fSWarner Losh DEVMETHOD(device_shutdown, sis_shutdown), 25227968da57SPyun YongHyeon DEVMETHOD(device_suspend, sis_suspend), 25237968da57SPyun YongHyeon DEVMETHOD(device_resume, sis_resume), 2524d2155f2fSWarner Losh 2525d2155f2fSWarner Losh /* bus interface */ 2526d2155f2fSWarner Losh DEVMETHOD(bus_print_child, bus_generic_print_child), 2527d2155f2fSWarner Losh DEVMETHOD(bus_driver_added, bus_generic_driver_added), 2528d2155f2fSWarner Losh 2529d2155f2fSWarner Losh /* MII interface */ 2530d2155f2fSWarner Losh DEVMETHOD(miibus_readreg, sis_miibus_readreg), 2531d2155f2fSWarner Losh DEVMETHOD(miibus_writereg, sis_miibus_writereg), 2532d2155f2fSWarner Losh DEVMETHOD(miibus_statchg, sis_miibus_statchg), 2533d2155f2fSWarner Losh 2534d2155f2fSWarner Losh { 0, 0 } 2535d2155f2fSWarner Losh }; 2536d2155f2fSWarner Losh 2537d2155f2fSWarner Losh static driver_t sis_driver = { 2538d2155f2fSWarner Losh "sis", 2539d2155f2fSWarner Losh sis_methods, 2540d2155f2fSWarner Losh sizeof(struct sis_softc) 2541d2155f2fSWarner Losh }; 2542d2155f2fSWarner Losh 2543d2155f2fSWarner Losh static devclass_t sis_devclass; 2544d2155f2fSWarner Losh 2545d2155f2fSWarner Losh DRIVER_MODULE(sis, pci, sis_driver, sis_devclass, 0, 0); 2546d2155f2fSWarner Losh DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); 2547