Hello, I'm trying to port the SiS190 driver from freeBSD. So far I've been quite successful. It compiles without warnings and finds the the MAC and in my case the Realtek PHY. Sadly I only have one PC with one NIC so I can do no real testing. My dsl modem with PPPoE does not yet work. Still I can see some activity on the link when I try to set it up. The driver is attached. It still needs some cleanup, but first I want to get working. Please read on if you have only little knowledge about the pci support in the openBSD kernel. I have none and probably only need a small hint.
It probably doesn't yet work because the driver is not able to read the MAC address. On linux the chip works and on dmesg I read: > sis190 Gigabit Ethernet driver 1.3 loaded. > alloc irq_desc for 19 on node -1 > alloc kstat_irqs on node -1 > sis190 0000:00:04.0: PCI INT A -> GSI 19 (level, low) -> IRQ 19 > sis190 0000:00:04.0: setting latency timer to 64 > 0000:00:04.0: Read MAC address from EEPROM > 0000:00:04.0: Error EEPROM read 0. Same as above on my ported driver for openBSD. > 0000:00:04.0: Read MAC address from APC. > 0000:00:04.0: Realtek PHY RTL8201 transceiver at address the relevant parts from the linux driver for "Read MAC address from APC" can be found below for you convenience. Now my problem is I have very little BSD experience. I now have used it only for four days and have no clue how to port this function. And weather similar functions/macros exist in openBSD. Any little hint is appreciated. Thanks. Cheers, Christopher Zimmermann rc = sis190_get_mac_addr_from_eeprom(pdev, dev); /* This seems to fail */ if (rc < 0) { u8 reg; pci_read_config_byte(pdev, 0x73, ®); /* How to do this in openBSD */ if (reg & 0x00000001) rc = sis190_get_mac_addr_from_apc(pdev, dev); } /** * sis190_get_mac_addr_from_apc - Get MAC address for SiS96x model * @pdev: PCI device * @dev: network device to get address for * * SiS96x model, use APC CMOS RAM to store MAC address. * APC CMOS RAM is accessed through ISA bridge. * MAC address is read into @net_dev->dev_addr. */ static int __devinit sis190_get_mac_addr_from_apc(struct pci_dev *pdev, struct net_device *dev) { static const u16 __devinitdata ids[] = { 0x0965, 0x0966, 0x0968 }; struct sis190_private *tp = netdev_priv(dev); struct pci_dev *isa_bridge; u8 reg, tmp8; unsigned int i; net_probe(tp, KERN_INFO "%s: Read MAC address from APC.\n", pci_name(pdev)); /* * Now this is the interesting part. How do I do this on openBSD? * Here's the linux source for pc_get_device: * ** * pci_get_device - begin or continue searching for a PCI device by vendor/device id * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is * found with a matching @vendor and @device, the reference count to the * device is incremented and a pointer to its device structure is returned. * Otherwise, %NULL is returned. A new search is initiated by passing %NULL * as the @from argument. Otherwise if @from is not %NULL, searches continue * from next device on the global list. The reference count for @from is * always decremented if it is not %NULL. */ struct pci_dev * pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) { return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); } for (i = 0; i < ARRAY_SIZE(ids); i++) { isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, ids[i], NULL); if (isa_bridge) break; } if (!isa_bridge) { net_probe(tp, KERN_INFO "%s: Can not find ISA bridge.\n", pci_name(pdev)); return -EIO; } /* Enable port 78h & 79h to access APC Registers. */ pci_read_config_byte(isa_bridge, 0x48, &tmp8); reg = (tmp8 & ~0x02); pci_write_config_byte(isa_bridge, 0x48, reg); udelay(50); pci_read_config_byte(isa_bridge, 0x48, ®); for (i = 0; i < MAC_ADDR_LEN; i++) { outb(0x9 + i, 0x78); dev->dev_addr[i] = inb(0x79); } outb(0x12, 0x78); reg = inb(0x79); sis190_set_rgmii(tp, reg); /* Restore the value to ISA Bridge */ pci_write_config_byte(isa_bridge, 0x48, tmp8); pci_dev_put(isa_bridge); return 0; }
/*- * Copyright (c) 2007, 2008 Alexander Pohoyda <alexander.poho...@gmx.net> * Copyright (c) 1997, 1998, 1999 * Bill Paul <wp...@ctr.columbia.edu>. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHORS OR * THE VOICES IN THEIR HEADS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> /* * SiS 190 Fast Ethernet PCI NIC driver. * * Adapted to SiS 190 NIC by Alexander Pohoyda based on the original * SiS 900 driver by Bill Paul, using SiS 190/191 Solaris driver by * Masayuki Murayama and SiS 190/191 GNU/Linux driver by K.M. Liu * <km...@sis.com>. Thanks to Pyun YongHyeon <pyu...@gmail.com> for * review and very useful comments. * * It should be easy to adapt this driver to SiS 191 Gigabit Ethernet * PCI NIC. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/sockio.h> #include <sys/endian.h> #include <sys/mbuf.h> #include <sys/malloc.h> #include <sys/kernel.h> #include <sys/socket.h> #include <net/if.h> #include <net/if_arp.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> #ifdef INET #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> #include <netinet/ip.h> #include <netinet/if_ether.h> #endif #include <net/bpf.h> #include <machine/bus.h> #include <dev/mii/mii.h> #include <dev/mii/miivar.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> #include "if_sis19xreg.h" /* "controller miibus0" required. See GENERIC if you get errors here. */ /* * Various supported device vendors/types and their names. */ static const struct pci_matchid sis_devices[] = { { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_190 }, /*{ PCI_VENDOR_SIS, PCI_PRODUCT_SIS_191 }*/ }; static int sis_probe(struct device *, void *, void *); static void sis_attach(struct device *, struct device *, void *); struct cfattach sis19x_ca = { sizeof(struct sis_softc), sis_probe, sis_attach }; struct cfdriver sis19x_cd = { 0, "sis19x", DV_IFNET }; static void sis_shutdown (void *); static int sis_miibus_readreg (struct device *, int, int); static void sis_miibus_writereg (struct device *, int, int, int); static void sis_miibus_statchg (struct device *); static int sis_newbuf (struct sis_softc *, u_int32_t, struct mbuf *); static int sis_encap (struct sis_softc *, struct mbuf *, u_int32_t *); static void sis_rxeof (struct sis_softc *); static void sis_txeof (struct sis_softc *); static int sis_intr (void *); static void sis_tick (void *); static void sis_start (struct ifnet *); static int sis_ioctl (struct ifnet *, u_long, caddr_t); static int sis_init (struct ifnet *); static void sis_stop (struct sis_softc *); static void sis_watchdog (struct ifnet *); static int sis_ifmedia_upd (struct ifnet *); static void sis_ifmedia_sts (struct ifnet *, struct ifmediareq *); static int sis_get_mac_addr_eeprom(struct sis_softc *, caddr_t); static uint16_t sis_read_eeprom (struct sis_softc *, int); static void sis_setmulti (struct sis_softc *); static void sis_reset (struct sis_softc *); static int sis_list_rx_init (struct sis_softc *); static int sis_list_rx_free (struct sis_softc *); static int sis_list_tx_init (struct sis_softc *); static int sis_list_tx_free (struct sis_softc *); static uint32_t sis_mchash (struct sis_softc *, const uint8_t *); #define SIS_PCI_LOMEM 0x10 #if 0 static struct resource_spec sis19x_res_spec[] = { { SYS_RES_MEMORY, 0x10, RF_ACTIVE}, { SYS_RES_IRQ, 0x00, RF_ACTIVE | RF_SHAREABLE}, { -1, 0 } }; static device_method_t sis19x_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sis_probe), DEVMETHOD(device_attach, sis_attach), DEVMETHOD(device_detach, sis_detach), DEVMETHOD(device_shutdown, sis_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sis_miibus_readreg), DEVMETHOD(miibus_writereg, sis_miibus_writereg), { 0, 0 } }; static driver_t sis19x_driver = { "sis19x", sis19x_methods, sizeof(struct sis_softc) }; #endif enum sis19x_registers { TxControl = 0x00, TxDescStartAddr = 0x04, Reserved0 = 0x08, // unused TxNextDescAddr = 0x0c, // unused RxControl = 0x10, RxDescStartAddr = 0x14, Reserved1 = 0x18, // unused RxNextDescAddr = 0x1c, // unused IntrStatus = 0x20, IntrMask = 0x24, IntrControl = 0x28, IntrTimer = 0x2c, // unused PMControl = 0x30, // unused Reserved2 = 0x34, // unused ROMControl = 0x38, ROMInterface = 0x3c, StationControl = 0x40, GMIIControl = 0x44, GMacIOCR = 0x48, GMacIOCTL = 0x4c, TxMacControl = 0x50, TxMacTimeLimit = 0x54, RGMIIDelay = 0x58, Reserved3 = 0x5c, RxMacControl = 0x60, // 1 WORD RxMacAddr = 0x62, // 6x BYTE RxHashTable = 0x68, // 1 LONG RxHashTable2 = 0x6c, // 1 LONG RxWakeOnLan = 0x70, RxWakeOnLanData = 0x74, RxMPSControl = 0x78, Reserved4 = 0x7c, }; enum sis19x_register_content { /* IntrStatus */ SoftInt = 0x40000000, // unused Timeup = 0x20000000, // unused PauseFrame = 0x00080000, // unused MagicPacket = 0x00040000, // unused WakeupFrame = 0x00020000, // unused LinkChange = 0x00010000, RxQEmpty = 0x00000080, //! RXIDLE RxQInt = 0x00000040, //! RXDONE TxQ1Empty = 0x00000020, // unused TxQ1Int = 0x00000010, // unused TxQEmpty = 0x00000008, //! TXIDLE TxQInt = 0x00000004, //! TXDONE RxHalt = 0x00000002, //! RXHALT TxHalt = 0x00000001, //! TXHALT /* RxStatusDesc */ RxRES = 0x00200000, // unused RxCRC = 0x00080000, RxRUNT = 0x00100000, // unused RxRWT = 0x00400000, // unused /* {Rx/Tx}CmdBits */ CmdReset = 0x10, CmdRxEnb = 0x01, /* Linux does not use it, but 0x8 */ CmdTxEnb = 0x01, /* RxMacControl */ AcceptBroadcast = 0x0800, AcceptMulticast = 0x0400, AcceptMyPhys = 0x0200, AcceptAllPhys = 0x0100, AcceptErr = 0x0020, // unused AcceptRunt = 0x0010, // unused }; /* fix KASSERT for two arguments */ #ifdef KASSERT #undef KASSERT #endif #define KASSERT(cond, complaint) if (!(cond)) panic complaint /* * register space access macros */ #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4(sc->sis_btag, sc->sis_bhandle, reg, val) #define CSR_WRITE_2(sc, reg, val) \ bus_space_write_2(sc->sis_btag, sc->sis_bhandle, reg, val) #define CSR_READ_4(sc, reg) \ bus_space_read_4(sc->sis_btag, sc->sis_bhandle, reg) #define CSR_READ_2(sc, reg) \ bus_space_read_2(sc->sis_btag, sc->sis_bhandle, reg) #define SIS_PCI_COMMIT() CSR_READ_4(sc, IntrControl) #define SIS_SETBIT(_sc, _reg, x) \ CSR_WRITE_4(_sc, _reg, CSR_READ_4(_sc, _reg) | (x)) #define SIS_CLRBIT(_sc, _reg, x) \ CSR_WRITE_4(_sc, _reg, CSR_READ_4(_sc, _reg) & ~(x)) #define DISABLE_INTERRUPTS(sc) CSR_WRITE_4(sc, IntrMask, 0x0) #define ENABLE_INTERRUPTS(sc) CSR_WRITE_4(sc, IntrMask, RxQEmpty | RxQInt | \ TxQInt | RxHalt | TxHalt) #define DEBUG #if defined(DEBUG) #define SIS_DEBUG(str, args...) \ device_printf(sc->sis_self, "%s: " str "\n", __func__, ## args) #else #define SIS_DEBUG(str, args...) #endif #define SIS_OUT(str, args...) \ printf(str "\n", ## args) /* * Gigabit Media Independent Interface CTL register */ #define GMI_DATA 0xffff0000 #define GMI_DATA_SHIFT 16 #define GMI_REG 0x0000f800 #define GMI_REG_SHIFT 11 #define GMI_PHY 0x000007c0 #define GMI_PHY_SHIFT 6 #define GMI_OP 0x00000020 #define GMI_OP_SHIFT 5 #define GMI_OP_WR (1 << GMI_OP_SHIFT) #define GMI_OP_RD (0 << GMI_OP_SHIFT) #define GMI_REQ 0x00000010 #define GMI_MDIO 0x00000008 /* not used */ #define GMI_MDDIR 0x00000004 /* not used */ #define GMI_MDC 0x00000002 /* not used */ #define GMI_MDEN 0x00000001 /* not used */ enum CommandStatus { OWNbit = 0x80000000, INTbit = 0x40000000, IPbit = 0x20000000, TCPbit = 0x10000000, UDPbit = 0x08000000, DEFbit = 0x00200000, CRCbit = 0x00020000, PADbit = 0x00010000, }; /* * RX descriptor status bits */ #define RDS_TAGON 0x80000000 #define RDS_DESCS 0x3f000000 #define RDS_ABORT 0x00800000 #define RDS_SHORT 0x00400000 #define RDS_LIMIT 0x00200000 #define RDS_MIIER 0x00100000 #define RDS_OVRUN 0x00080000 #define RDS_NIBON 0x00040000 #define RDS_COLON 0x00020000 #define RDS_CRCOK 0x00010000 #define RX_ERR_BITS \ (RDS_COLON | RDS_NIBON | RDS_OVRUN | RDS_MIIER | \ RDS_LIMIT | RDS_SHORT | RDS_ABORT) #define RING_END 0x80000000 #define SIS_RXSIZE(x) letoh32((x)->sis_sts_size & 0x0000ffff) #define SIS_RXSTATUS(x) letoh32((x)->sis_sts_size & 0xffff0000) #undef SIS_OWNDESC #define SIS_OWNDESC(x) ((x)->sis_cmdsts & OWNbit) #define SIS_INC(x, y) (x) = (((x) == ((y)-1)) ? 0 : (x)+1) /* * TX descriptor status bits */ #define TDS_OWC 0x00080000 #define TDS_ABT 0x00040000 #define TDS_FIFO 0x00020000 #define TDS_CRS 0x00010000 #define TDS_COLLS 0x0000ffff #define TX_ERR_BITS (TDS_OWC | TDS_ABT | TDS_FIFO | TDS_CRS) /* Taken from Solaris driver */ #define EI_DATA 0xffff0000 #define EI_DATA_SHIFT 16 #define EI_OFFSET 0x0000fc00 #define EI_OFFSET_SHIFT 10 #define EI_OP 0x00000300 #define EI_OP_SHIFT 8 #define EI_OP_RD (2 << EI_OP_SHIFT) #define EI_OP_WR (1 << EI_OP_SHIFT) #define EI_REQ 0x00000080 #define EI_DO 0x00000008 /* not used */ #define EI_DI 0x00000004 /* not used */ #define EI_CLK 0x00000002 /* not used */ #define EI_CS 0x00000001 /* * EEPROM Addresses */ #define EEPROMSignature 0x00 #define EEPROMCLK 0x01 #define EEPROMInfo 0x02 #define EEPROMMACAddr 0x03 /* * Read a sequence of words from the EEPROM. */ static uint16_t sis_read_eeprom(sc, offset) struct sis_softc *sc; int offset; { uint32_t ret, val, i; KASSERT(offset <= EI_OFFSET, ("EEPROM offset too big")); val = EI_REQ | EI_OP_RD | (offset << EI_OFFSET_SHIFT); CSR_WRITE_4(sc, ROMInterface, val); DELAY(500); for (i = 0; ((ret = CSR_READ_4(sc, ROMInterface)) & EI_REQ) != 0; i++) { if (i > SIS_TIMEOUT) { /* timeout */ SIS_OUT("EEPROM read timeout %d", i); return (0xffff); } DELAY(100); } return ((ret & EI_DATA) >> EI_DATA_SHIFT); } static int sis_get_mac_addr_eeprom(sc, dest) struct sis_softc *sc; caddr_t dest; { uint16_t val, i; val = sis_read_eeprom(sc, EEPROMSignature); if (val == 0xffff || val == 0x0000) { SIS_OUT("invalid EEPROM signature (%x)", val); return (1); } for (i = 0; i < ETHER_ADDR_LEN; i += 2) { val = sis_read_eeprom(sc, EEPROMMACAddr + i/2); dest[i + 0] = (uint8_t) val; dest[i + 1] = (uint8_t) (val >> 8); } return (0); } static void miibus_cmd(struct sis_softc *sc, u_int32_t ctl) { uint32_t i, ret; CSR_WRITE_4(sc, GMIIControl, ctl); DELAY(10); for (i = 0; ((ret = CSR_READ_4(sc, GMIIControl)) & GMI_REQ) != 0; i++) { if (i > SIS_TIMEOUT) { /* timeout */ SIS_OUT("MIIBUS timeout"); return; } DELAY(10); } } static int sis_miibus_readreg(self, phy, reg) struct device *self; int phy, reg; { struct sis_softc *sc = (struct sis_softc *)self; miibus_cmd(sc, (phy << GMI_PHY_SHIFT) | (reg << GMI_REG_SHIFT) | GMI_OP_RD | GMI_REQ); return ((CSR_READ_4(sc, GMIIControl) & GMI_DATA) >> GMI_DATA_SHIFT); } static void sis_miibus_writereg(self, phy, reg, data) struct device *self; int phy, reg, data; { struct sis_softc *sc = (struct sis_softc *)self; miibus_cmd(sc, (((uint32_t) data) << GMI_DATA_SHIFT) | (((uint32_t) phy) << GMI_PHY_SHIFT) | (reg << GMI_REG_SHIFT) | GMI_OP_WR | GMI_REQ); } static void sis_miibus_statchg(self) struct device *self; { struct sis_softc *sc = (struct sis_softc *)self; sis_init(sc->sis_ifp); } static u_int32_t sis_mchash(sc, addr) struct sis_softc *sc; const uint8_t *addr; { return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26); } static void sis_setmulti(sc) struct sis_softc *sc; { struct ifnet *ifp = sc->sis_ifp; struct arpcom *ac = &sc->arpcom; struct ether_multi *enm; struct ether_multistep step; uint32_t hashes[2] = { 0, 0 }, rxfilt; //struct ifmultiaddr *ifma; rxfilt = AcceptMyPhys | 0x0052; if (ifp->if_flags & IFF_PROMISC) { rxfilt |= AcceptAllPhys; } else { rxfilt &= ~AcceptAllPhys; } if (ifp->if_flags & IFF_BROADCAST) { rxfilt |= AcceptBroadcast; } else { rxfilt &= ~AcceptBroadcast; } if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { allmulti: rxfilt |= AcceptMulticast; CSR_WRITE_2(sc, RxMacControl, rxfilt); CSR_WRITE_4(sc, RxHashTable, 0xFFFFFFFF); CSR_WRITE_4(sc, RxHashTable2, 0xFFFFFFFF); return; } /* now program new ones */ //TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { ifp->if_flags |= IFF_ALLMULTI; goto allmulti; } int bit_nr = sis_mchash(sc, LLADDR((struct sockaddr_dl *) enm->enm_addrlo)); hashes[bit_nr >> 5] |= 1 << (bit_nr & 31); rxfilt |= AcceptMulticast; ETHER_NEXT_MULTI(step, enm); } CSR_WRITE_2(sc, RxMacControl, rxfilt); CSR_WRITE_4(sc, RxHashTable, hashes[0]); CSR_WRITE_4(sc, RxHashTable2, hashes[1]); } static void sis_reset(sc) struct sis_softc *sc; { CSR_WRITE_4(sc, IntrMask, 0); CSR_WRITE_4(sc, IntrStatus, 0xffffffff); CSR_WRITE_4(sc, TxControl, 0x00001a00); CSR_WRITE_4(sc, RxControl, 0x00001a1d); CSR_WRITE_4(sc, IntrControl, 0x8000); SIS_PCI_COMMIT(); DELAY(100); CSR_WRITE_4(sc, IntrControl, 0x0); CSR_WRITE_4(sc, IntrMask, 0); CSR_WRITE_4(sc, IntrStatus, 0xffffffff); } /* * Probe for an SiS chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int sis_probe(parent, match, aux) struct device *parent; void *match; void *aux; { return (pci_matchbyid((struct pci_attach_args *)aux, sis_devices, sizeof(sis_devices)/sizeof(sis_devices[0]))); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static void sis_attach(parent, self, aux) struct device *parent; struct device *self; void *aux; { const char *intrstr = NULL; struct sis_softc *sc = (struct sis_softc *)self; struct pci_attach_args *pa = aux; pci_chipset_tag_t pc = pa->pa_pc; pci_intr_handle_t ih; bus_size_t size; struct ifnet *ifp; bus_dma_segment_t seg; int nseg; struct sis19x_list_data *ld; struct sis19x_chain_data *cd; int i, error = 0; ld = &sc->sis_ldata; cd = &sc->sis_cdata; #if 0 if (PCI_PRODUCT(pa->pa_id) != SIS_TYPE_190) { printf("SiS191 is not supportet yet :-(\n"); error = ENXIO; goto fail; } #endif sc->sis_type = SIS_TYPE_190; sc->sis_rev = PCI_REVISION(pa->pa_class); /* XXX unused */ /* TODO: What about power management ? */ /* * Handle power management nonsense. */ #if 0 /* power management registers */ #define SIS_PCI_CAPID 0x50 /* 8 bits */ #define SIS_PCI_NEXTPTR 0x51 /* 8 bits */ #define SIS_PCI_PWRMGMTCAP 0x52 /* 16 bits */ #define SIS_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ #define SIS_PSTATE_MASK 0x0003 #define SIS_PSTATE_D0 0x0000 #define SIS_PSTATE_D1 0x0001 #define SIS_PSTATE_D2 0x0002 #define SIS_PSTATE_D3 0x0003 #define SIS_PME_EN 0x0010 #define SIS_PME_STATUS 0x8000 #define SIS_PCI_INTLINE 0x3C #define SIS_PCI_LOMEM 0x14 command = pci_conf_read(pc, pa->pa_tag, SIS_PCI_CAPID) & 0x000000FF; if (command == 0x01) { command = pci_conf_read(pc, pa->pa_tag, SIS_PCI_PWRMGMTCTRL); if (command & SIS_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ iobase = pci_conf_read(pc, pa->pa_tag, SIS_PCI_LOIO); membase = pci_conf_read(pc, pa->pa_tag, SIS_PCI_LOMEM); irq = pci_conf_read(pc, pa->pa_tag, SIS_PCI_INTLINE); /* Reset the power state. */ printf("%s: chip is in D%d power mode -- setting to D0\n", sc->sc_dev.dv_xname, command & SIS_PSTATE_MASK); command &= 0xFFFFFFFC; pci_conf_write(pc, pa->pa_tag, SIS_PCI_PWRMGMTCTRL, command); /* Restore PCI config data. */ pci_conf_write(pc, pa->pa_tag, SIS_PCI_LOIO, iobase); pci_conf_write(pc, pa->pa_tag, SIS_PCI_LOMEM, membase); pci_conf_write(pc, pa->pa_tag, SIS_PCI_INTLINE, irq); } } #endif /* * Map control/status registers. */ //pci_enable_busmaster(dev); /* Map IO */ if ((error = pci_mapreg_map(pa, SIS_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0, &sc->sis_btag, &sc->sis_bhandle, NULL, &size, 0))) { printf(": can't map i/o space (code %d)\n", error); return; } /* Allocate interrupt */ if (pci_intr_map(pa, &ih)) { printf(": couldn't map interrupt\n"); goto intfail; } intrstr = pci_intr_string(pc, ih); sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, sis_intr, sc, self->dv_xname); if (sc->sc_ih == NULL) { printf(": couldn't establish interrupt"); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); goto intfail; } /* Reset the adapter. */ sis_reset(sc); /* * Get MAC address from the EEPROM. */ sis_get_mac_addr_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr); printf(": %s, address %s\n", intrstr, ether_sprintf(sc->arpcom.ac_enaddr)); /* * Now do all the DMA mapping stuff */ sc->sis_tag = pa->pa_dmat; /* First create TX/RX busdma maps. */ for (i = 0; i < SIS_RX_RING_CNT; i++) { error = bus_dmamap_create(sc->sis_tag, MCLBYTES, 1, MCLBYTES, 0, BUS_DMA_NOWAIT, &cd->sis_rx_map[i]); if (error) { printf("cannot init the RX map array!\n"); goto fail; } } for (i = 0; i < SIS_TX_RING_CNT; i++) { error = bus_dmamap_create(sc->sis_tag, MCLBYTES, 1, MCLBYTES, 0, BUS_DMA_NOWAIT, &cd->sis_tx_map[i]); if (error) { printf("cannot init the TX map array!\n"); goto fail; } } /* * Now allocate a tag for the DMA descriptor lists and a chunk * of DMA-able memory based on the tag. Also obtain the physical * addresses of the RX and TX ring, which we'll need later. * All of our lists are allocated as a contiguous block * of memory. */ /* RX */ error = bus_dmamem_alloc(sc->sis_tag, SIS_RX_RING_SZ, PAGE_SIZE, 0, &seg, 1, &nseg, BUS_DMA_NOWAIT); if (error) { printf("no memory for rx list buffers!\n"); goto fail; } error = bus_dmamem_map(sc->sis_tag, &seg, nseg, SIS_RX_RING_SZ, (caddr_t *)&ld->sis_rx_ring, BUS_DMA_NOWAIT); if (error) { printf("can't map rx list buffers!\n"); goto fail; } error = bus_dmamap_create(sc->sis_tag, SIS_RX_RING_SZ, 1, SIS_RX_RING_SZ, 0, BUS_DMA_NOWAIT, &ld->sis_rx_dmamap); if (error) { printf("can't alloc rx list map!\n"); goto fail; } error = bus_dmamap_load(sc->sis_tag, ld->sis_rx_dmamap, (caddr_t)ld->sis_rx_ring, SIS_RX_RING_SZ, NULL, BUS_DMA_NOWAIT); if (error) { printf("cannot load rx ring mapping!\n"); bus_dmamem_unmap(sc->sis_tag, (caddr_t)ld->sis_rx_ring, SIS_RX_RING_SZ); bus_dmamap_destroy(sc->sis_tag, ld->sis_rx_dmamap); bus_dmamem_free(sc->sis_tag, &seg, nseg); goto fail; } /* TX */ error = bus_dmamem_alloc(sc->sis_tag, SIS_TX_RING_SZ, PAGE_SIZE, 0, &seg, 1, &nseg, BUS_DMA_NOWAIT); if (error) { printf("no memory for tx list buffers!\n"); goto fail; } error = bus_dmamem_map(sc->sis_tag, &seg, nseg, SIS_TX_RING_SZ, (caddr_t *)&ld->sis_tx_ring, BUS_DMA_NOWAIT); if (error) { printf("can't map tx list buffers!\n"); goto fail; } error = bus_dmamap_create(sc->sis_tag, SIS_TX_RING_SZ, 1, SIS_TX_RING_SZ, 0, BUS_DMA_NOWAIT, &ld->sis_tx_dmamap); if (error) { printf("can't alloc tx list map!\n"); goto fail; } error = bus_dmamap_load(sc->sis_tag, ld->sis_tx_dmamap, (caddr_t)ld->sis_tx_ring, SIS_TX_RING_SZ, NULL, BUS_DMA_NOWAIT); if (error) { printf("cannot load tx ring mapping!\n"); bus_dmamem_unmap(sc->sis_tag, (caddr_t)ld->sis_tx_ring, SIS_TX_RING_SZ); bus_dmamap_destroy(sc->sis_tag, ld->sis_tx_dmamap); bus_dmamem_free(sc->sis_tag, &seg, nseg); goto fail; } timeout_set(&sc->sis_timeout, sis_tick, sc); sc->sis_ifp = ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; /* do I really need to set this? if_sis.c doesn't */ ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /*ifp->if_capenable = ifp->if_capabilities; XXX only in fBSD */ ifp->if_ioctl = sis_ioctl; ifp->if_start = sis_start; ifp->if_watchdog = sis_watchdog; ifp->if_baudrate = 10000000; /* XXX 10MBit ?!? */ ifp->if_init = sis_init; /* XXX not in if_sis.c */ IFQ_SET_MAXLEN(&ifp->if_snd, SIS_TX_RING_CNT - 1); IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); /* * Do MII setup. */ sc->sc_mii.mii_ifp = ifp; sc->sc_mii.mii_readreg = sis_miibus_readreg; sc->sc_mii.mii_writereg = sis_miibus_writereg; sc->sc_mii.mii_statchg = sis_miibus_statchg; ifmedia_init(&sc->sc_mii.mii_media, 0, sis_ifmedia_upd,sis_ifmedia_sts); /* TODO: This won't work. mii_phy_probe is of type void. */ mii_phy_probe(self, &sc->sc_mii, 0xffffffff); if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); } else ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); /* * Call MI attach routine. */ if_attach(ifp); ether_ifattach(ifp); shutdownhook_establish(sis_shutdown, sc); fail: pci_intr_disestablish(pc, sc->sc_ih); intfail: bus_space_unmap(sc->sis_btag, sc->sis_bhandle, size); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void sis_shutdown(v) void *v; { struct sis_softc *sc = (struct sis_softc *)v; sis_reset(sc); sis_stop(sc); } /* * Initialize the TX descriptors. */ static int sis_list_tx_init(sc) struct sis_softc *sc; { struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; bzero(ld->sis_tx_ring, SIS_TX_RING_SZ); ld->sis_tx_ring[SIS_TX_RING_CNT - 1].sis_flags |= RING_END; cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; return (0); } static int sis_list_tx_free(sc) struct sis_softc *sc; { struct sis19x_chain_data *cd = &sc->sis_cdata; register int i; for (i = 0; i < SIS_TX_RING_CNT; i++) { if (cd->sis_tx_mbuf[i] != NULL) { bus_dmamap_unload(sc->sis_tag, cd->sis_tx_map[i]); m_free(cd->sis_tx_mbuf[i]); cd->sis_tx_mbuf[i] = NULL; } } return (0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * has RING_END flag set. */ static int sis_list_rx_init(sc) struct sis_softc *sc; { struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; register int i; bzero(ld->sis_rx_ring, SIS_RX_RING_SZ); for (i = 0; i < SIS_RX_RING_CNT; i++) { if (sis_newbuf(sc, i, NULL) == ENOBUFS) { SIS_OUT("unable to allocate MBUFs, %d", i); return (ENOBUFS); } } ld->sis_rx_ring[SIS_RX_RING_CNT - 1].sis_flags |= RING_END; cd->sis_rx_prod = 0; return (0); } static int sis_list_rx_free(sc) struct sis_softc *sc; { struct sis19x_chain_data *cd = &sc->sis_cdata; register int i; for (i = 0; i < SIS_RX_RING_CNT; i++) { if (cd->sis_rx_mbuf[i] != NULL) { bus_dmamap_unload(sc->sis_tag, cd->sis_rx_map[i]); m_free(cd->sis_rx_mbuf[i]); cd->sis_rx_mbuf[i] = NULL; } } return (0); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int sis_newbuf(sc, i, m) struct sis_softc *sc; u_int32_t i; struct mbuf *m; { struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; int error, alloc; if (m == NULL) { m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { SIS_OUT("unable to get new MBUF"); return (ENOBUFS); } cd->sis_rx_mbuf[i] = m; alloc = 1; } else { m->m_data = m->m_ext.ext_buf; alloc = 0; } m->m_len = m->m_pkthdr.len = MCLBYTES; if (alloc) { error = bus_dmamap_load_mbuf(sc->sis_tag, cd->sis_rx_map[i], m, BUS_DMA_NOWAIT); if (error) { SIS_OUT("unable to map and load the MBUF"); m_freem(m); return (ENOBUFS); } } /* This is used both to initialize the newly created RX * descriptor as well as for re-initializing it for reuse. */ ld->sis_rx_ring[i].sis_sts_size = 0; ld->sis_rx_ring[i].sis_cmdsts = htole32(OWNbit | INTbit | IPbit | TCPbit | UDPbit); ld->sis_rx_ring[i].sis_ptr = htole32(cd->sis_rx_map[i]->dm_segs[0].ds_addr); ld->sis_rx_ring[i].sis_flags = htole32(cd->sis_rx_map[i]->dm_segs[0].ds_len); KASSERT(cd->sis_rx_map[i]->dm_nsegs == 1, ("wrong number of segments, should be 1")); bus_dmamap_sync(sc->sis_tag, cd->sis_rx_map[i], 0, cd->sis_rx_map[i]->dm_mapsize, BUS_DMASYNC_PREREAD); return (0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void sis_rxeof(sc) struct sis_softc *sc; { struct mbuf *m, *m0; struct ifnet *ifp = sc->sis_ifp; struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; struct sis19x_desc *cur_rx; u_int32_t i, rxstat, total_len = 0; for (i = cd->sis_rx_prod; !SIS_OWNDESC(&ld->sis_rx_ring[i]); SIS_INC(i, SIS_RX_RING_CNT)) { bus_dmamap_sync(sc->sis_tag, cd->sis_rx_map[i], 0, cd->sis_rx_map[i]->dm_mapsize, BUS_DMASYNC_POSTREAD); cur_rx = &ld->sis_rx_ring[i]; rxstat = SIS_RXSTATUS(cur_rx); total_len = SIS_RXSIZE(cur_rx); m = cd->sis_rx_mbuf[i]; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & RX_ERR_BITS) { SIS_OUT("error_bits=%#x", rxstat); ifp->if_ierrors++; /* TODO: better error differentiation */ sis_newbuf(sc, i, m); continue; } /* No errors; receive the packet. */ #if 0 //def __NO_STRICT_ALIGNMENT if (sis_newbuf(sc, i, NULL) == 0) { m->m_pkthdr.len = m->m_len = total_len; } else #endif { m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); sis_newbuf(sc, i, m); if (m0 == NULL) { SIS_OUT("unable to copy MBUF"); ifp->if_ierrors++; continue; } m = m0; } ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); #endif /* pass it on. */ ether_input_mbuf(ifp, m); } cd->sis_rx_prod = i; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void sis_txeof(sc) struct sis_softc *sc; { struct ifnet *ifp = sc->sis_ifp; struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; struct sis19x_desc *cur_tx; u_int32_t i, txstat; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ for (i = cd->sis_tx_cons; cd->sis_tx_cnt > 0 && !SIS_OWNDESC(&ld->sis_tx_ring[i]); cd->sis_tx_cnt--, SIS_INC(i, SIS_TX_RING_CNT)) { cur_tx = &ld->sis_tx_ring[i]; txstat = letoh32(cur_tx->sis_cmdsts); bus_dmamap_sync(sc->sis_tag, cd->sis_tx_map[i], 0, cd->sis_tx_map[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); /* current slot is transferred now */ if (txstat & TX_ERR_BITS) { SIS_OUT("error_bits=%#x", txstat); ifp->if_oerrors++; /* TODO: better error differentiation */ } ifp->if_opackets++; if (cd->sis_tx_mbuf[i] != NULL) { bus_dmamap_unload(sc->sis_tag, cd->sis_tx_map[i]); m_free(cd->sis_tx_mbuf[i]); cd->sis_tx_mbuf[i] = NULL; } cur_tx->sis_sts_size = 0; cur_tx->sis_cmdsts = 0; cur_tx->sis_ptr = 0; cur_tx->sis_flags &= RING_END; } if (i != cd->sis_tx_cons) { /* we freed up some buffers */ cd->sis_tx_cons = i; ifp->if_flags &= ~IFF_OACTIVE; } sc->sis_watchdog_timer = (cd->sis_tx_cnt == 0) ? 0 : 5; } static void sis_tick(xsc) void *xsc; { struct sis_softc *sc = xsc; struct mii_data *mii; struct ifnet *ifp = sc->sis_ifp; sc->in_tick = 1; mii = &sc->sc_mii; mii_tick(mii); sis_watchdog(ifp); if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sis_link++; if (!IFQ_IS_EMPTY(&ifp->if_snd)) sis_start(ifp); } timeout_add_sec(&sc->sis_timeout, 1); sc->in_tick = 0; } static int sis_intr(arg) void *arg; { struct sis_softc *sc = arg; struct ifnet *ifp = sc->sis_ifp; int status; int claimed = 0; if (sc->sis_stopped) /* Most likely shared interrupt */ return (claimed); DISABLE_INTERRUPTS(sc); for (;;) { /* Reading the ISR register clears all interrupts. */ status = CSR_READ_4(sc, IntrStatus); if ((status == 0xffffffff) || (status == 0x0)) break; claimed = 1; /* XXX just a guess to put this here */ CSR_WRITE_4(sc, IntrStatus, status); if (status & TxQInt) sis_txeof(sc); if (status & RxQInt) sis_rxeof(sc); } ENABLE_INTERRUPTS(sc); if (!IFQ_IS_EMPTY(&ifp->if_snd)) sis_start(ifp); return (claimed); } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int sis_encap(sc, m_head, txidx) struct sis_softc *sc; struct mbuf *m_head; u_int32_t *txidx; { struct mbuf *m; struct sis19x_list_data *ld = &sc->sis_ldata; struct sis19x_chain_data *cd = &sc->sis_cdata; int error, i, cnt = 0; /* * If there's no way we can send any packets, return now. */ if (SIS_TX_RING_CNT - cd->sis_tx_cnt < 2) return (ENOBUFS); if(m_defrag(m_head, M_DONTWAIT)) { SIS_OUT("unable to defragment MBUFs"); return (ENOBUFS); } /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ i = *txidx; for (m = m_head; m != NULL; m = m->m_next) { if (m->m_len == 0) continue; if ((SIS_TX_RING_CNT - (cd->sis_tx_cnt + cnt)) < 2) return (ENOBUFS); cd->sis_tx_mbuf[i] = m; error = bus_dmamap_load_mbuf(sc->sis_tag, cd->sis_tx_map[i], m, BUS_DMA_NOWAIT); if (error) { SIS_OUT("unable to load the MBUF"); return (ENOBUFS); } ld->sis_tx_ring[i].sis_sts_size = htole32(cd->sis_tx_map[i]->dm_segs->ds_len); ld->sis_tx_ring[i].sis_cmdsts = htole32(OWNbit | INTbit | PADbit | CRCbit | DEFbit); ld->sis_tx_ring[i].sis_ptr = htole32(cd->sis_tx_map[i]->dm_segs->ds_addr); ld->sis_tx_ring[i].sis_flags |= htole32(cd->sis_tx_map[i]->dm_segs->ds_len); KASSERT(cd->sis_tx_map[i]->dm_nsegs == 1, ("wrong number of segments, should be 1")); bus_dmamap_sync(sc->sis_tag, cd->sis_tx_map[i], 0, cd->sis_tx_map[i]->dm_mapsize, BUS_DMASYNC_PREWRITE); SIS_INC(i, SIS_TX_RING_CNT); cnt++; } if (m != NULL) { SIS_OUT("unable to encap all MBUFs"); return (ENOBUFS); } cd->sis_tx_cnt += cnt; *txidx = i; return (0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void sis_start(ifp) struct ifnet *ifp; { struct sis_softc *sc = ifp->if_softc; struct mbuf *m_head = NULL; struct sis19x_chain_data *cd = &sc->sis_cdata; u_int32_t i, queued = 0; if (!sc->sis_link) { return; } if (ifp->if_flags & IFF_OACTIVE) { return; } i = cd->sis_tx_prod; while (cd->sis_tx_mbuf[i] == NULL) { IFQ_POLL(&ifp->if_snd, m_head); if (m_head == NULL) break; if (sis_encap(sc, m_head, &i)) { ifp->if_flags |= IFF_OACTIVE; break; } /* now we are committed to transmit the packet */ IFQ_DEQUEUE(&ifp->if_snd, m_head); queued++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); #endif } if (queued) { /* Transmit */ cd->sis_tx_prod = i; SIS_SETBIT(sc, TxControl, CmdReset); /* * Set a timeout in case the chip goes out to lunch. */ sc->sis_watchdog_timer = 5; } } /* TODO: Find out right return codes */ static int sis_init(ifp) struct ifnet *ifp; { struct sis_softc *sc = ifp->if_softc; /* * Cancel pending I/O and free all RX/TX buffers. */ sis_stop(sc); sc->sis_stopped = 0; /* Init circular RX list. */ if (sis_list_rx_init(sc) == ENOBUFS) { printf("initialization failed: no " "memory for rx buffers\n"); sis_stop(sc); return 1; } /* Init TX descriptors. */ sis_list_tx_init(sc); sis_reset(sc); /* * Load the address of the RX and TX lists. */ CSR_WRITE_4(sc, TxDescStartAddr, sc->sis_ldata.sis_tx_dmamap->dm_segs[0].ds_addr); CSR_WRITE_4(sc, RxDescStartAddr, sc->sis_ldata.sis_rx_dmamap->dm_segs[0].ds_addr); /* CSR_WRITE_4(sc, PMControl, 0xffc00000); */ CSR_WRITE_4(sc, Reserved2, 0); CSR_WRITE_4(sc, IntrStatus, 0xffffffff); DISABLE_INTERRUPTS(sc); /* * Default is 100Mbps. * A bit strange: 100Mbps is 0x1801 elsewhere -- FR 2005/06/09 */ CSR_WRITE_4(sc, StationControl, 0x04001801); // 1901 CSR_WRITE_4(sc, GMacIOCR, 0x0); CSR_WRITE_4(sc, GMacIOCTL, 0x0); CSR_WRITE_4(sc, TxMacControl, 0x2364); /* 0x60 */ CSR_WRITE_4(sc, TxMacTimeLimit, 0x000f); CSR_WRITE_4(sc, RGMIIDelay, 0x0); CSR_WRITE_4(sc, Reserved3, 0x0); CSR_WRITE_4(sc, RxWakeOnLan, 0x80ff0000); CSR_WRITE_4(sc, RxWakeOnLanData, 0x80ff0000); CSR_WRITE_4(sc, RxMPSControl, 0x0); CSR_WRITE_4(sc, Reserved4, 0x0); SIS_PCI_COMMIT(); /* * Load the multicast filter. */ sis_setmulti(sc); /* * Enable interrupts. */ ENABLE_INTERRUPTS(sc); /* Enable receiver and transmitter. */ SIS_SETBIT(sc, TxControl, CmdTxEnb | CmdReset); SIS_SETBIT(sc, RxControl, CmdRxEnb | CmdReset); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; if (!sc->in_tick) timeout_add_sec(&sc->sis_timeout, 1); return 0; } /* * Set media options. */ static int sis_ifmedia_upd(ifp) struct ifnet *ifp; { struct sis_softc *sc = ifp->if_softc; struct mii_data *mii; mii = &sc->sc_mii; sc->sis_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return (0); } /* * Report current media status. */ static void sis_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sis_softc *sc = ifp->if_softc; struct mii_data *mii; mii = &sc->sc_mii; mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int sis_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sis_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) sis_init(ifp); else if (ifp->if_flags & IFF_RUNNING) sis_stop(sc); error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: sis_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = &sc->sc_mii; error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, &sc->arpcom, command, data); break; } return (error); } static void sis_watchdog(ifp) struct ifnet *ifp; { struct sis_softc *sc = ifp->if_softc; if (sc->sis_stopped) return; if (sc->sis_watchdog_timer == 0 || --sc->sis_watchdog_timer >0) return; SIS_OUT("watchdog timeout"); sc->sis_ifp->if_oerrors++; sis_stop(sc); sis_reset(sc); sis_init(ifp); if (!IFQ_IS_EMPTY(&sc->sis_ifp->if_snd)) sis_start(sc->sis_ifp); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void sis_stop(sc) struct sis_softc *sc; { struct ifnet *ifp = sc->sis_ifp; if (sc->sis_stopped) return; sc->sis_watchdog_timer = 0; timeout_del(&sc->sis_timeout); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); DISABLE_INTERRUPTS(sc); CSR_WRITE_4(sc, IntrControl, 0x8000); SIS_PCI_COMMIT(); DELAY(100); CSR_WRITE_4(sc, IntrControl, 0x0); SIS_CLRBIT(sc, TxControl, CmdTxEnb); SIS_CLRBIT(sc, RxControl, CmdRxEnb); DELAY(100); CSR_WRITE_4(sc, TxDescStartAddr, 0); CSR_WRITE_4(sc, RxDescStartAddr, 0); sc->sis_link = 0; /* * Free data in the RX lists. */ sis_list_rx_free(sc); /* * Free the TX list buffers. */ sis_list_tx_free(sc); sc->sis_stopped = 1; }
/*- * Copyright (c) 2007, 2008 Alexander Pohoyda <alexander.poho...@gmx.net> * Copyright (c) 1997, 1998, 1999 * Bill Paul <wp...@ctr.columbia.edu>. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHORS OR * THE VOICES IN THEIR HEADS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ struct sis19x_desc { volatile u_int32_t sis_sts_size; volatile u_int32_t sis_cmdsts; volatile u_int32_t sis_ptr; volatile u_int32_t sis_flags; }; #define SIS_RX_RING_CNT 256 /* [8, 1024] */ #define SIS_TX_RING_CNT 256 /* [8, 8192] */ #define SIS_RX_RING_SZ (SIS_RX_RING_CNT * sizeof(struct sis19x_desc)) #define SIS_TX_RING_SZ (SIS_TX_RING_CNT * sizeof(struct sis19x_desc)) struct sis19x_list_data { struct sis19x_desc *sis_rx_ring; struct sis19x_desc *sis_tx_ring; bus_dmamap_t sis_rx_dmamap; bus_dmamap_t sis_tx_dmamap; }; struct sis19x_chain_data { int sis_rx_prod; int sis_tx_prod; int sis_tx_cons; int sis_tx_cnt; struct mbuf *sis_rx_mbuf[SIS_RX_RING_CNT]; struct mbuf *sis_tx_mbuf[SIS_TX_RING_CNT]; bus_dmamap_t sis_rx_map[SIS_RX_RING_CNT]; bus_dmamap_t sis_tx_map[SIS_TX_RING_CNT]; }; /* * SiS PCI vendor ID. */ #define SIS_VENDORID 0x1039 /* * SiS PCI device IDs */ #define SIS_DEVICEID_190 0x0190 #define SIS_DEVICEID_191 0x0191 struct sis_type { u_int16_t sis_vid; u_int16_t sis_did; char *sis_name; }; #define SIS_TYPE_190 5 struct sis_softc { struct device sc_dev; void *sc_ih; struct ifnet *sis_ifp; /* interface info */ struct resource *sis_res[2]; void *sis_intrhand; mii_data_t sc_mii; struct arpcom arpcom; bus_space_handle_t sis_bhandle; bus_space_tag_t sis_btag; u_int8_t sis_type; u_int8_t sis_rev; u_int8_t sis_link; struct timeout sis_timeout; bus_dma_tag_t sis_tag; struct sis19x_list_data sis_ldata; struct sis19x_chain_data sis_cdata; int sis_watchdog_timer; int sis_stopped; int in_tick; }; #define SIS_TIMEOUT 1000 #define ETHER_ALIGN 2
signature.asc
Description: PGP signature