2010-04-05 11:19:28 +04:00
|
|
|
/* $NetBSD: if_ti.c,v 1.89 2010/04/05 07:20:27 joerg Exp $ */
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1997, 1998, 1999
|
|
|
|
* Bill Paul <wpaul@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 Bill Paul OR THE VOICES IN HIS HEAD
|
|
|
|
* 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 Id: if_ti.c,v 1.15 1999/08/14 15:45:03 wpaul Exp
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alteon Networks Tigon PCI gigabit ethernet driver for FreeBSD.
|
|
|
|
* Manuals, sample driver and firmware source kits are available
|
|
|
|
* from http://www.alteon.com/support/openkits.
|
2005-02-27 03:26:58 +03:00
|
|
|
*
|
1999-09-01 15:47:46 +04:00
|
|
|
* Written by Bill Paul <wpaul@ctr.columbia.edu>
|
|
|
|
* Electrical Engineering Department
|
|
|
|
* Columbia University, New York City
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Alteon Networks Tigon chip contains an embedded R4000 CPU,
|
|
|
|
* gigabit MAC, dual DMA channels and a PCI interface unit. NICs
|
|
|
|
* using the Tigon may have anywhere from 512K to 2MB of SRAM. The
|
|
|
|
* Tigon supports hardware IP, TCP and UCP checksumming, multicast
|
|
|
|
* filtering and jumbo (9014 byte) frames. The hardware is largely
|
|
|
|
* controlled by firmware, which must be loaded into the NIC during
|
|
|
|
* initialization.
|
|
|
|
*
|
|
|
|
* The Tigon 2 contains 2 R4000 CPUs and requires a newer firmware
|
|
|
|
* revision, which supports new features such as extended commands,
|
|
|
|
* extended jumbo receive ring desciptors and a mini receive ring.
|
|
|
|
*
|
|
|
|
* Alteon Networks is to be commended for releasing such a vast amount
|
|
|
|
* of development material for the Tigon NIC without requiring an NDA
|
|
|
|
* (although they really should have done it a long time ago). With
|
|
|
|
* any luck, the other vendors will finally wise up and follow Alteon's
|
|
|
|
* stellar example.
|
|
|
|
*
|
|
|
|
* The firmware for the Tigon 1 and 2 NICs is compiled directly into
|
|
|
|
* this driver by #including it as a C header file. This bloats the
|
|
|
|
* driver somewhat, but it's the easiest method considering that the
|
|
|
|
* driver code and firmware code need to be kept in sync. The source
|
|
|
|
* for the firmware is not provided with the FreeBSD distribution since
|
|
|
|
* compiling it requires a GNU toolchain targeted for mips-sgi-irix5.3.
|
|
|
|
*
|
|
|
|
* The following people deserve special thanks:
|
|
|
|
* - Terry Murphy of 3Com, for providing a 3c985 Tigon 1 board
|
|
|
|
* for testing
|
|
|
|
* - Raymond Lee of Netgear, for providing a pair of Netgear
|
|
|
|
* GA620 Tigon 2 boards for testing
|
2000-01-25 23:19:14 +03:00
|
|
|
* - Ulf Zimmermann, for bringing the GA620 to my attention and
|
1999-09-01 15:47:46 +04:00
|
|
|
* convincing me to write this driver.
|
|
|
|
* - Andrew Gallatin for providing FreeBSD/Alpha support.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:48:40 +03:00
|
|
|
#include <sys/cdefs.h>
|
2010-04-05 11:19:28 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: if_ti.c,v 1.89 2010/04/05 07:20:27 joerg Exp $");
|
2001-11-13 10:48:40 +03:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
#include "opt_inet.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/device.h>
|
2000-09-24 16:37:03 +04:00
|
|
|
#include <sys/reboot.h>
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2000-11-14 21:42:55 +03:00
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/if_ether.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/if_inarp.h>
|
2001-06-02 20:17:06 +04:00
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
1999-09-01 15:47:46 +04:00
|
|
|
#endif
|
|
|
|
|
1999-10-06 15:23:33 +04:00
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/bus.h>
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
|
|
|
|
#include <dev/pci/if_tireg.h>
|
2001-06-30 08:33:11 +04:00
|
|
|
|
|
|
|
#include <dev/microcode/tigon/ti_fw.h>
|
|
|
|
#include <dev/microcode/tigon/ti_fw2.h>
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Various supported device vendors/types and their names.
|
|
|
|
*/
|
|
|
|
|
2001-01-18 23:28:15 +03:00
|
|
|
static const struct ti_type ti_devs[] = {
|
1999-09-01 15:47:46 +04:00
|
|
|
{ PCI_VENDOR_ALTEON, PCI_PRODUCT_ALTEON_ACENIC,
|
2001-07-06 01:27:31 +04:00
|
|
|
"Alteon AceNIC 1000BASE-SX Ethernet" },
|
2000-11-17 22:33:25 +03:00
|
|
|
{ PCI_VENDOR_ALTEON, PCI_PRODUCT_ALTEON_ACENIC_COPPER,
|
2001-07-06 01:27:31 +04:00
|
|
|
"Alteon AceNIC 1000BASE-T Ethernet" },
|
1999-09-01 15:47:46 +04:00
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C985,
|
|
|
|
"3Com 3c985-SX Gigabit Ethernet" },
|
|
|
|
{ PCI_VENDOR_NETGEAR, PCI_PRODUCT_NETGEAR_GA620,
|
2001-07-06 01:27:31 +04:00
|
|
|
"Netgear GA620 1000BASE-SX Ethernet" },
|
2000-11-17 22:33:25 +03:00
|
|
|
{ PCI_VENDOR_NETGEAR, PCI_PRODUCT_NETGEAR_GA620T,
|
2001-07-06 01:27:31 +04:00
|
|
|
"Netgear GA620 1000BASE-T Ethernet" },
|
1999-09-01 15:47:46 +04:00
|
|
|
{ PCI_VENDOR_SGI, PCI_PRODUCT_SGI_TIGON,
|
|
|
|
"Silicon Graphics Gigabit Ethernet" },
|
|
|
|
{ 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
static const struct ti_type *ti_type_match(struct pci_attach_args *);
|
2009-05-06 14:34:32 +04:00
|
|
|
static int ti_probe(device_t, cfdata_t, void *);
|
|
|
|
static void ti_attach(device_t, device_t, void *);
|
2009-09-27 16:52:59 +04:00
|
|
|
static bool ti_shutdown(device_t, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void ti_txeof_tigon1(struct ti_softc *);
|
|
|
|
static void ti_txeof_tigon2(struct ti_softc *);
|
|
|
|
static void ti_rxeof(struct ti_softc *);
|
|
|
|
|
|
|
|
static void ti_stats_update(struct ti_softc *);
|
|
|
|
static int ti_encap_tigon1(struct ti_softc *, struct mbuf *, u_int32_t *);
|
|
|
|
static int ti_encap_tigon2(struct ti_softc *, struct mbuf *, u_int32_t *);
|
|
|
|
|
|
|
|
static int ti_intr(void *);
|
|
|
|
static void ti_start(struct ifnet *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static int ti_ioctl(struct ifnet *, u_long, void *);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void ti_init(void *);
|
|
|
|
static void ti_init2(struct ti_softc *);
|
|
|
|
static void ti_stop(struct ti_softc *);
|
|
|
|
static void ti_watchdog(struct ifnet *);
|
|
|
|
static int ti_ifmedia_upd(struct ifnet *);
|
|
|
|
static void ti_ifmedia_sts(struct ifnet *, struct ifmediareq *);
|
|
|
|
|
|
|
|
static u_int32_t ti_eeprom_putbyte(struct ti_softc *, int);
|
|
|
|
static u_int8_t ti_eeprom_getbyte(struct ti_softc *, int, u_int8_t *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static int ti_read_eeprom(struct ti_softc *, void *, int, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
|
|
|
|
static void ti_add_mcast(struct ti_softc *, struct ether_addr *);
|
|
|
|
static void ti_del_mcast(struct ti_softc *, struct ether_addr *);
|
|
|
|
static void ti_setmulti(struct ti_softc *);
|
|
|
|
|
2005-05-30 08:35:22 +04:00
|
|
|
static void ti_mem(struct ti_softc *, u_int32_t, u_int32_t, const void *);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void ti_loadfw(struct ti_softc *);
|
|
|
|
static void ti_cmd(struct ti_softc *, struct ti_cmd_desc *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static void ti_cmd_ext(struct ti_softc *, struct ti_cmd_desc *, void *, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void ti_handle_events(struct ti_softc *);
|
|
|
|
static int ti_alloc_jumbo_mem(struct ti_softc *);
|
|
|
|
static void *ti_jalloc(struct ti_softc *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static void ti_jfree(struct mbuf *, void *, size_t, void *);
|
2005-02-04 05:10:35 +03:00
|
|
|
static int ti_newbuf_std(struct ti_softc *, int, struct mbuf *, bus_dmamap_t);
|
|
|
|
static int ti_newbuf_mini(struct ti_softc *, int, struct mbuf *, bus_dmamap_t);
|
|
|
|
static int ti_newbuf_jumbo(struct ti_softc *, int, struct mbuf *);
|
|
|
|
static int ti_init_rx_ring_std(struct ti_softc *);
|
|
|
|
static void ti_free_rx_ring_std(struct ti_softc *);
|
|
|
|
static int ti_init_rx_ring_jumbo(struct ti_softc *);
|
|
|
|
static void ti_free_rx_ring_jumbo(struct ti_softc *);
|
|
|
|
static int ti_init_rx_ring_mini(struct ti_softc *);
|
|
|
|
static void ti_free_rx_ring_mini(struct ti_softc *);
|
|
|
|
static void ti_free_tx_ring(struct ti_softc *);
|
|
|
|
static int ti_init_tx_ring(struct ti_softc *);
|
|
|
|
|
|
|
|
static int ti_64bitslot_war(struct ti_softc *);
|
|
|
|
static int ti_chipinit(struct ti_softc *);
|
|
|
|
static int ti_gibinit(struct ti_softc *);
|
|
|
|
|
2007-03-04 08:59:00 +03:00
|
|
|
static int ti_ether_ioctl(struct ifnet *, u_long, void *);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2002-10-01 00:37:04 +04:00
|
|
|
CFATTACH_DECL(ti, sizeof(struct ti_softc),
|
2002-10-02 20:51:16 +04:00
|
|
|
ti_probe, ti_attach, NULL, NULL);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send an instruction or address to the EEPROM, check for ACK.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static u_int32_t
|
|
|
|
ti_eeprom_putbyte(struct ti_softc *sc, int byte)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2005-02-04 05:10:35 +03:00
|
|
|
int i, ack = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we're in TX mode.
|
|
|
|
*/
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Feed in each bit and stobe the clock.
|
|
|
|
*/
|
|
|
|
for (i = 0x80; i; i >>= 1) {
|
|
|
|
if (byte & i) {
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT);
|
|
|
|
} else {
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_DOUT);
|
|
|
|
}
|
|
|
|
DELAY(1);
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
DELAY(1);
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Turn off TX mode.
|
|
|
|
*/
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for ack.
|
|
|
|
*/
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
ack = CSR_READ_4(sc, TI_MISC_LOCAL_CTL) & TI_MLC_EE_DIN;
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ack);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a byte of data stored in the EEPROM at address 'addr.'
|
|
|
|
* We have to send two address bytes since the EEPROM can hold
|
|
|
|
* more than 256 bytes of data.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static u_int8_t
|
|
|
|
ti_eeprom_getbyte(struct ti_softc *sc, int addr, u_int8_t *dest)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
u_int8_t byte = 0;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
EEPROM_START();
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send write control code to EEPROM.
|
|
|
|
*/
|
|
|
|
if (ti_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) {
|
|
|
|
printf("%s: failed to send write command, status: %x\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), CSR_READ_4(sc, TI_MISC_LOCAL_CTL));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (1);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send first byte of address of byte we want to read.
|
|
|
|
*/
|
|
|
|
if (ti_eeprom_putbyte(sc, (addr >> 8) & 0xFF)) {
|
|
|
|
printf("%s: failed to send address, status: %x\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), CSR_READ_4(sc, TI_MISC_LOCAL_CTL));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (1);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Send second byte address of byte we want to read.
|
|
|
|
*/
|
|
|
|
if (ti_eeprom_putbyte(sc, addr & 0xFF)) {
|
|
|
|
printf("%s: failed to send address, status: %x\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), CSR_READ_4(sc, TI_MISC_LOCAL_CTL));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (1);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
EEPROM_STOP();
|
|
|
|
EEPROM_START();
|
1999-09-01 15:47:46 +04:00
|
|
|
/*
|
|
|
|
* Send read control code to EEPROM.
|
|
|
|
*/
|
|
|
|
if (ti_eeprom_putbyte(sc, EEPROM_CTL_READ)) {
|
|
|
|
printf("%s: failed to send read command, status: %x\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), CSR_READ_4(sc, TI_MISC_LOCAL_CTL));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (1);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start reading bits from EEPROM.
|
|
|
|
*/
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_TXEN);
|
|
|
|
for (i = 0x80; i; i >>= 1) {
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
DELAY(1);
|
|
|
|
if (CSR_READ_4(sc, TI_MISC_LOCAL_CTL) & TI_MLC_EE_DIN)
|
|
|
|
byte |= i;
|
|
|
|
TI_CLRBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_EE_CLK);
|
|
|
|
DELAY(1);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
EEPROM_STOP();
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* No ACK generated for read, so just return byte.
|
|
|
|
*/
|
|
|
|
|
|
|
|
*dest = byte;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a sequence of bytes from the EEPROM.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_read_eeprom(struct ti_softc *sc, void *destv, int off, int cnt)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2007-03-04 08:59:00 +03:00
|
|
|
char *dest = destv;
|
|
|
|
int err = 0, i;
|
|
|
|
u_int8_t byte = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
err = ti_eeprom_getbyte(sc, off + i, &byte);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
*(dest + i) = byte;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (err ? 1 : 0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NIC memory access function. Can be used to either clear a section
|
2005-05-30 08:35:22 +04:00
|
|
|
* of NIC local memory or (if tbuf is non-NULL) copy data into it.
|
1999-09-01 15:47:46 +04:00
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_mem(struct ti_softc *sc, u_int32_t addr, u_int32_t len, const void *xbuf)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
int segptr, segsize, cnt;
|
2005-05-30 08:35:22 +04:00
|
|
|
const void *ptr;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
segptr = addr;
|
|
|
|
cnt = len;
|
2005-05-30 08:35:22 +04:00
|
|
|
ptr = xbuf;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
while (cnt) {
|
1999-09-01 15:47:46 +04:00
|
|
|
if (cnt < TI_WINLEN)
|
|
|
|
segsize = cnt;
|
|
|
|
else
|
|
|
|
segsize = TI_WINLEN - (segptr % TI_WINLEN);
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1)));
|
2005-05-30 08:35:22 +04:00
|
|
|
if (xbuf == NULL) {
|
2000-03-17 12:20:16 +03:00
|
|
|
bus_space_set_region_4(sc->ti_btag, sc->ti_bhandle,
|
|
|
|
TI_WINDOW + (segptr & (TI_WINLEN - 1)), 0,
|
|
|
|
segsize / 4);
|
|
|
|
} else {
|
2004-03-19 02:20:32 +03:00
|
|
|
#ifdef __BUS_SPACE_HAS_STREAM_METHODS
|
|
|
|
bus_space_write_region_stream_4(sc->ti_btag,
|
|
|
|
sc->ti_bhandle,
|
|
|
|
TI_WINDOW + (segptr & (TI_WINLEN - 1)),
|
2005-05-30 08:35:22 +04:00
|
|
|
(const u_int32_t *)ptr, segsize / 4);
|
2004-03-19 02:20:32 +03:00
|
|
|
#else
|
2000-03-17 12:20:16 +03:00
|
|
|
bus_space_write_region_4(sc->ti_btag, sc->ti_bhandle,
|
|
|
|
TI_WINDOW + (segptr & (TI_WINLEN - 1)),
|
2005-05-30 08:35:22 +04:00
|
|
|
(const u_int32_t *)ptr, segsize / 4);
|
2004-03-19 02:20:32 +03:00
|
|
|
#endif
|
2005-05-30 08:35:22 +04:00
|
|
|
ptr = (const char *)ptr + segsize;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
segptr += segsize;
|
|
|
|
cnt -= segsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load firmware image into the NIC. Check that the firmware revision
|
|
|
|
* is acceptable and see if we want the firmware for the Tigon 1 or
|
|
|
|
* Tigon 2.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_loadfw(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (sc->ti_hwrev) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case TI_HWREV_TIGON:
|
|
|
|
if (tigonFwReleaseMajor != TI_FIRMWARE_MAJOR ||
|
|
|
|
tigonFwReleaseMinor != TI_FIRMWARE_MINOR ||
|
|
|
|
tigonFwReleaseFix != TI_FIRMWARE_FIX) {
|
|
|
|
printf("%s: firmware revision mismatch; want "
|
2008-04-10 23:13:36 +04:00
|
|
|
"%d.%d.%d, got %d.%d.%d\n", device_xname(&sc->sc_dev),
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_FIRMWARE_MAJOR, TI_FIRMWARE_MINOR,
|
|
|
|
TI_FIRMWARE_FIX, tigonFwReleaseMajor,
|
|
|
|
tigonFwReleaseMinor, tigonFwReleaseFix);
|
|
|
|
return;
|
|
|
|
}
|
2005-05-30 08:35:22 +04:00
|
|
|
ti_mem(sc, tigonFwTextAddr, tigonFwTextLen, tigonFwText);
|
|
|
|
ti_mem(sc, tigonFwDataAddr, tigonFwDataLen, tigonFwData);
|
|
|
|
ti_mem(sc, tigonFwRodataAddr, tigonFwRodataLen, tigonFwRodata);
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_mem(sc, tigonFwBssAddr, tigonFwBssLen, NULL);
|
|
|
|
ti_mem(sc, tigonFwSbssAddr, tigonFwSbssLen, NULL);
|
|
|
|
CSR_WRITE_4(sc, TI_CPU_PROGRAM_COUNTER, tigonFwStartAddr);
|
|
|
|
break;
|
|
|
|
case TI_HWREV_TIGON_II:
|
|
|
|
if (tigon2FwReleaseMajor != TI_FIRMWARE_MAJOR ||
|
|
|
|
tigon2FwReleaseMinor != TI_FIRMWARE_MINOR ||
|
|
|
|
tigon2FwReleaseFix != TI_FIRMWARE_FIX) {
|
|
|
|
printf("%s: firmware revision mismatch; want "
|
2008-04-10 23:13:36 +04:00
|
|
|
"%d.%d.%d, got %d.%d.%d\n", device_xname(&sc->sc_dev),
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_FIRMWARE_MAJOR, TI_FIRMWARE_MINOR,
|
|
|
|
TI_FIRMWARE_FIX, tigon2FwReleaseMajor,
|
|
|
|
tigon2FwReleaseMinor, tigon2FwReleaseFix);
|
|
|
|
return;
|
|
|
|
}
|
2005-05-30 08:35:22 +04:00
|
|
|
ti_mem(sc, tigon2FwTextAddr, tigon2FwTextLen, tigon2FwText);
|
|
|
|
ti_mem(sc, tigon2FwDataAddr, tigon2FwDataLen, tigon2FwData);
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_mem(sc, tigon2FwRodataAddr, tigon2FwRodataLen,
|
2005-05-30 08:35:22 +04:00
|
|
|
tigon2FwRodata);
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_mem(sc, tigon2FwBssAddr, tigon2FwBssLen, NULL);
|
|
|
|
ti_mem(sc, tigon2FwSbssAddr, tigon2FwSbssLen, NULL);
|
|
|
|
CSR_WRITE_4(sc, TI_CPU_PROGRAM_COUNTER, tigon2FwStartAddr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: can't load firmware: unknown hardware rev\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the NIC a command via the command ring.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_cmd(struct ti_softc *sc, struct ti_cmd_desc *cmd)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
u_int32_t index;
|
|
|
|
|
|
|
|
index = sc->ti_cmd_saved_prodidx;
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), *(u_int32_t *)(cmd));
|
|
|
|
TI_INC(index, TI_CMD_RING_CNT);
|
|
|
|
CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, index);
|
|
|
|
sc->ti_cmd_saved_prodidx = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the NIC an extended command. The 'len' parameter specifies the
|
|
|
|
* number of command slots to include after the initial command.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_cmd_ext(struct ti_softc *sc, struct ti_cmd_desc *cmd, void *argv, int len)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2007-03-04 08:59:00 +03:00
|
|
|
char *arg = argv;
|
1999-09-01 15:47:46 +04:00
|
|
|
u_int32_t index;
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
index = sc->ti_cmd_saved_prodidx;
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4), *(u_int32_t *)(cmd));
|
|
|
|
TI_INC(index, TI_CMD_RING_CNT);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_CMDRING + (index * 4),
|
|
|
|
*(u_int32_t *)(&arg[i * 4]));
|
|
|
|
TI_INC(index, TI_CMD_RING_CNT);
|
|
|
|
}
|
|
|
|
CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, index);
|
|
|
|
sc->ti_cmd_saved_prodidx = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle events that have triggered interrupts.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_handle_events(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_event_desc *e;
|
|
|
|
|
|
|
|
if (sc->ti_rdata->ti_event_ring == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (sc->ti_ev_saved_considx != sc->ti_ev_prodidx.ti_idx) {
|
|
|
|
e = &sc->ti_rdata->ti_event_ring[sc->ti_ev_saved_considx];
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (TI_EVENT_EVENT(e)) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case TI_EV_LINKSTAT_CHANGED:
|
2007-09-08 03:05:27 +04:00
|
|
|
sc->ti_linkstat = TI_EVENT_CODE(e);
|
|
|
|
if (sc->ti_linkstat == TI_EV_CODE_LINK_UP)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: 10/100 link up\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
else if (sc->ti_linkstat == TI_EV_CODE_GIG_LINK_UP)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: gigabit link up\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
else if (sc->ti_linkstat == TI_EV_CODE_LINK_DOWN)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: link down\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
case TI_EV_ERROR:
|
2007-09-08 03:05:27 +04:00
|
|
|
if (TI_EVENT_CODE(e) == TI_EV_CODE_ERR_INVAL_CMD)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: invalid command\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
else if (TI_EVENT_CODE(e) == TI_EV_CODE_ERR_UNIMP_CMD)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: unknown command\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
else if (TI_EVENT_CODE(e) == TI_EV_CODE_ERR_BADCFG)
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("%s: bad config data\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
case TI_EV_FIRMWARE_UP:
|
|
|
|
ti_init2(sc);
|
|
|
|
break;
|
|
|
|
case TI_EV_STATS_UPDATED:
|
|
|
|
ti_stats_update(sc);
|
|
|
|
break;
|
|
|
|
case TI_EV_RESET_JUMBO_RING:
|
|
|
|
case TI_EV_MCAST_UPDATED:
|
|
|
|
/* Who cares. */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: unknown event: %d\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), TI_EVENT_EVENT(e));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Advance the consumer index. */
|
|
|
|
TI_INC(sc->ti_ev_saved_considx, TI_EVENT_RING_CNT);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_EVENTCONS_IDX, sc->ti_ev_saved_considx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Memory management for the jumbo receive ring is a pain in the
|
|
|
|
* butt. We need to allocate at least 9018 bytes of space per frame,
|
|
|
|
* _and_ it has to be contiguous (unless you use the extended
|
|
|
|
* jumbo descriptor format). Using malloc() all the time won't
|
|
|
|
* work: malloc() allocates memory in powers of two, which means we
|
|
|
|
* would end up wasting a considerable amount of space by allocating
|
|
|
|
* 9K chunks. We don't have a jumbo mbuf cluster pool. Thus, we have
|
|
|
|
* to do our own memory management.
|
|
|
|
*
|
|
|
|
* The driver needs to allocate a contiguous chunk of memory at boot
|
|
|
|
* time. We then chop this up ourselves into 9K pieces and use them
|
|
|
|
* as external mbuf storage.
|
|
|
|
*
|
|
|
|
* One issue here is how much memory to allocate. The jumbo ring has
|
|
|
|
* 256 slots in it, but at 9K per slot than can consume over 2MB of
|
|
|
|
* RAM. This is a bit much, especially considering we also need
|
|
|
|
* RAM for the standard ring and mini ring (on the Tigon 2). To
|
|
|
|
* save space, we only actually allocate enough memory for 64 slots
|
|
|
|
* by default, which works out to between 500 and 600K. This can
|
|
|
|
* be tuned by changing a #define in if_tireg.h.
|
|
|
|
*/
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_alloc_jumbo_mem(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2007-03-04 08:59:00 +03:00
|
|
|
char *ptr;
|
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ti_jpool_entry *entry;
|
|
|
|
bus_dma_segment_t dmaseg;
|
|
|
|
int error, dmanseg;
|
|
|
|
|
|
|
|
/* Grab a big chunk o' storage. */
|
|
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat,
|
2000-11-14 21:42:55 +03:00
|
|
|
TI_JMEM, PAGE_SIZE, 0, &dmaseg, 1, &dmanseg,
|
1999-09-01 15:47:46 +04:00
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't allocate jumbo buffer, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &dmaseg, dmanseg,
|
2007-03-04 08:59:00 +03:00
|
|
|
TI_JMEM, (void **)&sc->ti_cdata.ti_jumbo_buf,
|
1999-09-01 15:47:46 +04:00
|
|
|
BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't map jumbo buffer, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat,
|
|
|
|
TI_JMEM, 1,
|
|
|
|
TI_JMEM, 0, BUS_DMA_NOWAIT,
|
|
|
|
&sc->jumbo_dmamap)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't create jumbo buffer DMA map, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->jumbo_dmamap,
|
|
|
|
sc->ti_cdata.ti_jumbo_buf, TI_JMEM, NULL,
|
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't load jumbo buffer DMA map, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
sc->jumbo_dmaaddr = sc->jumbo_dmamap->dm_segs[0].ds_addr;
|
|
|
|
|
|
|
|
SIMPLEQ_INIT(&sc->ti_jfree_listhead);
|
|
|
|
SIMPLEQ_INIT(&sc->ti_jinuse_listhead);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now divide it up into 9K pieces and save the addresses
|
2000-11-17 22:33:25 +03:00
|
|
|
* in an array.
|
1999-09-01 15:47:46 +04:00
|
|
|
*/
|
|
|
|
ptr = sc->ti_cdata.ti_jumbo_buf;
|
|
|
|
for (i = 0; i < TI_JSLOTS; i++) {
|
2000-11-17 22:33:25 +03:00
|
|
|
sc->ti_cdata.ti_jslots[i] = ptr;
|
|
|
|
ptr += TI_JLEN;
|
2005-02-27 03:26:58 +03:00
|
|
|
entry = malloc(sizeof(struct ti_jpool_entry),
|
1999-09-01 15:47:46 +04:00
|
|
|
M_DEVBUF, M_NOWAIT);
|
|
|
|
if (entry == NULL) {
|
|
|
|
free(sc->ti_cdata.ti_jumbo_buf, M_DEVBUF);
|
|
|
|
sc->ti_cdata.ti_jumbo_buf = NULL;
|
|
|
|
printf("%s: no memory for jumbo "
|
2008-04-10 23:13:36 +04:00
|
|
|
"buffer queue!\n", device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
entry->slot = i;
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->ti_jfree_listhead, entry,
|
|
|
|
jpool_entries);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a jumbo buffer.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void *
|
|
|
|
ti_jalloc(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_jpool_entry *entry;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
entry = SIMPLEQ_FIRST(&sc->ti_jfree_listhead);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
if (entry == NULL) {
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: no free jumbo buffers\n", device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (NULL);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->ti_jfree_listhead, jpool_entries);
|
1999-09-01 15:47:46 +04:00
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->ti_jinuse_listhead, entry, jpool_entries);
|
2007-09-08 03:05:27 +04:00
|
|
|
|
|
|
|
return (sc->ti_cdata.ti_jslots[entry->slot]);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release a jumbo buffer.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_jfree(struct mbuf *m, void *tbuf, size_t size, void *arg)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
2002-05-02 20:22:43 +04:00
|
|
|
int i, s;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ti_jpool_entry *entry;
|
|
|
|
|
|
|
|
/* Extract the softc struct pointer. */
|
2000-11-17 22:33:25 +03:00
|
|
|
sc = (struct ti_softc *)arg;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
if (sc == NULL)
|
2000-11-17 22:33:25 +03:00
|
|
|
panic("ti_jfree: didn't get softc pointer!");
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* calculate the slot this buffer belongs to */
|
|
|
|
|
2007-03-04 08:59:00 +03:00
|
|
|
i = ((char *)tbuf
|
|
|
|
- (char *)sc->ti_cdata.ti_jumbo_buf) / TI_JLEN;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
if ((i < 0) || (i >= TI_JSLOTS))
|
|
|
|
panic("ti_jfree: asked to free buffer that we don't manage!");
|
2002-05-02 20:22:43 +04:00
|
|
|
|
|
|
|
s = splvm();
|
2000-11-17 22:33:25 +03:00
|
|
|
entry = SIMPLEQ_FIRST(&sc->ti_jinuse_listhead);
|
|
|
|
if (entry == NULL)
|
|
|
|
panic("ti_jfree: buffer not in use!");
|
|
|
|
entry->slot = i;
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->ti_jinuse_listhead, jpool_entries);
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->ti_jfree_listhead, entry, jpool_entries);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2002-05-02 20:22:43 +04:00
|
|
|
if (__predict_true(m != NULL))
|
2007-11-07 03:23:13 +03:00
|
|
|
pool_cache_put(mb_cache, m);
|
2002-05-02 20:22:43 +04:00
|
|
|
splx(s);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Intialize a standard receive ring descriptor.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_newbuf_std(struct ti_softc *sc, int i, struct mbuf *m, bus_dmamap_t dmamap)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct mbuf *m_new = NULL;
|
|
|
|
struct ti_rx_desc *r;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (dmamap == NULL) {
|
|
|
|
/* if (m) panic() */
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
|
|
|
|
MCLBYTES, 0, BUS_DMA_NOWAIT,
|
|
|
|
&dmamap)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't create recv map, error = %d\n",
|
|
|
|
error);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOMEM);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->std_dmamap[i] = dmamap;
|
|
|
|
|
|
|
|
if (m == NULL) {
|
|
|
|
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m_new == NULL) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "mbuf allocation failed "
|
|
|
|
"-- packet dropped!\n");
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
MCLGET(m_new, M_DONTWAIT);
|
|
|
|
if (!(m_new->m_flags & M_EXT)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "cluster allocation failed "
|
|
|
|
"-- packet dropped!\n");
|
1999-09-01 15:47:46 +04:00
|
|
|
m_freem(m_new);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
mtod(m_new, void *), m_new->m_len, NULL,
|
2001-07-19 20:36:14 +04:00
|
|
|
BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't load recv map, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_new = m;
|
|
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
|
|
m_new->m_data = m_new->m_ext.ext_buf;
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
|
|
|
|
|
|
/* reuse the dmamap */
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->ti_cdata.ti_rx_std_chain[i] = m_new;
|
|
|
|
r = &sc->ti_rdata->ti_rx_std_ring[i];
|
|
|
|
TI_HOSTADDR(r->ti_addr) = dmamap->dm_segs[0].ds_addr;
|
|
|
|
r->ti_type = TI_BDTYPE_RECV_BD;
|
|
|
|
r->ti_flags = 0;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (sc->ethercom.ec_if.if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_IP_CKSUM;
|
|
|
|
if (sc->ethercom.ec_if.if_capenable &
|
2005-05-02 19:34:31 +04:00
|
|
|
(IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM;
|
1999-09-01 15:47:46 +04:00
|
|
|
r->ti_len = m_new->m_len; /* == ds_len */
|
|
|
|
r->ti_idx = i;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Intialize a mini receive ring descriptor. This only applies to
|
|
|
|
* the Tigon 2.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_newbuf_mini(struct ti_softc *sc, int i, struct mbuf *m, bus_dmamap_t dmamap)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct mbuf *m_new = NULL;
|
|
|
|
struct ti_rx_desc *r;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (dmamap == NULL) {
|
|
|
|
/* if (m) panic() */
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MHLEN, 1,
|
|
|
|
MHLEN, 0, BUS_DMA_NOWAIT,
|
|
|
|
&dmamap)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't create recv map, error = %d\n",
|
|
|
|
error);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOMEM);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->mini_dmamap[i] = dmamap;
|
|
|
|
|
|
|
|
if (m == NULL) {
|
|
|
|
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m_new == NULL) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "mbuf allocation failed "
|
|
|
|
"-- packet dropped!\n");
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
m_new->m_len = m_new->m_pkthdr.len = MHLEN;
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
mtod(m_new, void *), m_new->m_len, NULL,
|
2001-07-19 20:36:14 +04:00
|
|
|
BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't load recv map, error = %d\n",
|
|
|
|
error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_new = m;
|
|
|
|
m_new->m_data = m_new->m_pktdat;
|
|
|
|
m_new->m_len = m_new->m_pkthdr.len = MHLEN;
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
|
|
|
|
|
|
/* reuse the dmamap */
|
|
|
|
}
|
|
|
|
|
|
|
|
r = &sc->ti_rdata->ti_rx_mini_ring[i];
|
|
|
|
sc->ti_cdata.ti_rx_mini_chain[i] = m_new;
|
|
|
|
TI_HOSTADDR(r->ti_addr) = dmamap->dm_segs[0].ds_addr;
|
|
|
|
r->ti_type = TI_BDTYPE_RECV_BD;
|
|
|
|
r->ti_flags = TI_BDFLAG_MINI_RING;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (sc->ethercom.ec_if.if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_IP_CKSUM;
|
|
|
|
if (sc->ethercom.ec_if.if_capenable &
|
2005-05-02 19:34:31 +04:00
|
|
|
(IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM;
|
1999-09-01 15:47:46 +04:00
|
|
|
r->ti_len = m_new->m_len; /* == ds_len */
|
|
|
|
r->ti_idx = i;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a jumbo receive ring descriptor. This allocates
|
|
|
|
* a jumbo buffer from the pool managed internally by the driver.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_newbuf_jumbo(struct ti_softc *sc, int i, struct mbuf *m)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct mbuf *m_new = NULL;
|
|
|
|
struct ti_rx_desc *r;
|
|
|
|
|
|
|
|
if (m == NULL) {
|
2007-03-04 08:59:00 +03:00
|
|
|
void * tbuf = NULL;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Allocate the mbuf. */
|
|
|
|
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m_new == NULL) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "mbuf allocation failed "
|
|
|
|
"-- packet dropped!\n");
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the jumbo buffer */
|
2005-05-30 08:35:22 +04:00
|
|
|
tbuf = ti_jalloc(sc);
|
|
|
|
if (tbuf == NULL) {
|
1999-09-01 15:47:46 +04:00
|
|
|
m_freem(m_new);
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "jumbo allocation failed "
|
|
|
|
"-- packet dropped!\n");
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Attach the buffer to the mbuf. */
|
2005-05-30 08:35:22 +04:00
|
|
|
MEXTADD(m_new, tbuf, ETHER_MAX_LEN_JUMBO,
|
2002-04-28 05:00:26 +04:00
|
|
|
M_DEVBUF, ti_jfree, sc);
|
2004-09-22 01:57:30 +04:00
|
|
|
m_new->m_flags |= M_EXT_RW;
|
2002-04-28 05:00:26 +04:00
|
|
|
m_new->m_len = m_new->m_pkthdr.len = ETHER_MAX_LEN_JUMBO;
|
1999-09-01 15:47:46 +04:00
|
|
|
} else {
|
|
|
|
m_new = m;
|
|
|
|
m_new->m_data = m_new->m_ext.ext_buf;
|
2001-06-03 07:29:44 +04:00
|
|
|
m_new->m_ext.ext_size = ETHER_MAX_LEN_JUMBO;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
|
|
|
/* Set up the descriptor. */
|
|
|
|
r = &sc->ti_rdata->ti_rx_jumbo_ring[i];
|
|
|
|
sc->ti_cdata.ti_rx_jumbo_chain[i] = m_new;
|
|
|
|
TI_HOSTADDR(r->ti_addr) = sc->jumbo_dmaaddr +
|
2007-03-04 08:59:00 +03:00
|
|
|
(mtod(m_new, char *) - (char *)sc->ti_cdata.ti_jumbo_buf);
|
1999-09-01 15:47:46 +04:00
|
|
|
r->ti_type = TI_BDTYPE_RECV_JUMBO_BD;
|
|
|
|
r->ti_flags = TI_BDFLAG_JUMBO_RING;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (sc->ethercom.ec_if.if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_IP_CKSUM;
|
|
|
|
if (sc->ethercom.ec_if.if_capenable &
|
2005-05-02 19:34:31 +04:00
|
|
|
(IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM;
|
1999-09-01 15:47:46 +04:00
|
|
|
r->ti_len = m_new->m_len;
|
|
|
|
r->ti_idx = i;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The standard receive ring has 512 entries in it. At 2K per mbuf cluster,
|
|
|
|
* that's 1MB or memory, which is a lot. For now, we fill only the first
|
|
|
|
* 256 ring entries and hope that our CPU is fast enough to keep up with
|
|
|
|
* the NIC.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_init_rx_ring_std(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
|
|
|
for (i = 0; i < TI_SSLOTS; i++) {
|
|
|
|
if (ti_newbuf_std(sc, i, NULL, 0) == ENOBUFS)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
TI_UPDATE_STDPROD(sc, i - 1);
|
|
|
|
sc->ti_std = i - 1;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_free_rx_ring_std(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
for (i = 0; i < TI_STD_RX_RING_CNT; i++) {
|
|
|
|
if (sc->ti_cdata.ti_rx_std_chain[i] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_rx_std_chain[i]);
|
|
|
|
sc->ti_cdata.ti_rx_std_chain[i] = NULL;
|
|
|
|
|
|
|
|
/* if (sc->std_dmamap[i] == 0) panic() */
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->std_dmamap[i]);
|
|
|
|
sc->std_dmamap[i] = 0;
|
|
|
|
}
|
2001-07-07 20:46:34 +04:00
|
|
|
memset((char *)&sc->ti_rdata->ti_rx_std_ring[i], 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
sizeof(struct ti_rx_desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_init_rx_ring_jumbo(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
2004-07-27 11:59:51 +04:00
|
|
|
for (i = 0; i < TI_JUMBO_RX_RING_CNT; i++) {
|
1999-09-01 15:47:46 +04:00
|
|
|
if (ti_newbuf_jumbo(sc, i, NULL) == ENOBUFS)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
TI_UPDATE_JUMBOPROD(sc, i - 1);
|
|
|
|
sc->ti_jumbo = i - 1;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_free_rx_ring_jumbo(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
for (i = 0; i < TI_JUMBO_RX_RING_CNT; i++) {
|
|
|
|
if (sc->ti_cdata.ti_rx_jumbo_chain[i] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_rx_jumbo_chain[i]);
|
|
|
|
sc->ti_cdata.ti_rx_jumbo_chain[i] = NULL;
|
|
|
|
}
|
2001-07-07 20:46:34 +04:00
|
|
|
memset((char *)&sc->ti_rdata->ti_rx_jumbo_ring[i], 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
sizeof(struct ti_rx_desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_init_rx_ring_mini(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
for (i = 0; i < TI_MSLOTS; i++) {
|
|
|
|
if (ti_newbuf_mini(sc, i, NULL, 0) == ENOBUFS)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
TI_UPDATE_MINIPROD(sc, i - 1);
|
|
|
|
sc->ti_mini = i - 1;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_free_rx_ring_mini(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
for (i = 0; i < TI_MINI_RX_RING_CNT; i++) {
|
|
|
|
if (sc->ti_cdata.ti_rx_mini_chain[i] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_rx_mini_chain[i]);
|
|
|
|
sc->ti_cdata.ti_rx_mini_chain[i] = NULL;
|
|
|
|
|
|
|
|
/* if (sc->mini_dmamap[i] == 0) panic() */
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->mini_dmamap[i]);
|
|
|
|
sc->mini_dmamap[i] = 0;
|
|
|
|
}
|
2001-07-07 20:46:34 +04:00
|
|
|
memset((char *)&sc->ti_rdata->ti_rx_mini_ring[i], 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
sizeof(struct ti_rx_desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_free_tx_ring(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
int i;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct txdmamap_pool_entry *dma;
|
|
|
|
|
|
|
|
if (sc->ti_rdata->ti_tx_ring == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < TI_TX_RING_CNT; i++) {
|
|
|
|
if (sc->ti_cdata.ti_tx_chain[i] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_tx_chain[i]);
|
|
|
|
sc->ti_cdata.ti_tx_chain[i] = NULL;
|
|
|
|
|
|
|
|
/* if (sc->txdma[i] == 0) panic() */
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->txdma_list, sc->txdma[i],
|
|
|
|
link);
|
|
|
|
sc->txdma[i] = 0;
|
|
|
|
}
|
2001-07-07 20:46:34 +04:00
|
|
|
memset((char *)&sc->ti_rdata->ti_tx_ring[i], 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
sizeof(struct ti_tx_desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((dma = SIMPLEQ_FIRST(&sc->txdma_list))) {
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->txdma_list, link);
|
1999-09-01 15:47:46 +04:00
|
|
|
bus_dmamap_destroy(sc->sc_dmat, dma->dmamap);
|
|
|
|
free(dma, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_init_tx_ring(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
int i, error;
|
|
|
|
bus_dmamap_t dmamap;
|
|
|
|
struct txdmamap_pool_entry *dma;
|
|
|
|
|
|
|
|
sc->ti_txcnt = 0;
|
|
|
|
sc->ti_tx_saved_considx = 0;
|
|
|
|
CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, 0);
|
|
|
|
|
|
|
|
SIMPLEQ_INIT(&sc->txdma_list);
|
|
|
|
for (i = 0; i < TI_RSLOTS; i++) {
|
|
|
|
/* I've seen mbufs with 30 fragments. */
|
2001-06-03 07:29:44 +04:00
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, ETHER_MAX_LEN_JUMBO,
|
|
|
|
40, ETHER_MAX_LEN_JUMBO, 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
BUS_DMA_NOWAIT, &dmamap)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't create tx map, error = %d\n",
|
|
|
|
error);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOMEM);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
dma = malloc(sizeof(*dma), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (!dma) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't alloc txdmamap_pool_entry\n");
|
1999-09-01 15:47:46 +04:00
|
|
|
bus_dmamap_destroy(sc->sc_dmat, dmamap);
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
dma->dmamap = dmamap;
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->txdma_list, dma, link);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Tigon 2 firmware has a new way to add/delete multicast addresses,
|
|
|
|
* but we have to support the old way too so that Tigon 1 cards will
|
|
|
|
* work.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_add_mcast(struct ti_softc *sc, struct ether_addr *addr)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
u_int16_t *m;
|
|
|
|
u_int32_t ext[2] = {0, 0};
|
|
|
|
|
|
|
|
m = (u_int16_t *)&addr->ether_addr_octet[0]; /* XXX */
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (sc->ti_hwrev) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case TI_HWREV_TIGON:
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_MAR0, htons(m[0]));
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_MAR1, (htons(m[1]) << 16) | htons(m[2]));
|
|
|
|
TI_DO_CMD(TI_CMD_ADD_MCAST_ADDR, 0, 0);
|
|
|
|
break;
|
|
|
|
case TI_HWREV_TIGON_II:
|
|
|
|
ext[0] = htons(m[0]);
|
|
|
|
ext[1] = (htons(m[1]) << 16) | htons(m[2]);
|
2007-03-04 08:59:00 +03:00
|
|
|
TI_DO_CMD_EXT(TI_CMD_EXT_ADD_MCAST, 0, 0, (void *)&ext, 2);
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
default:
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: unknown hwrev\n", device_xname(&sc->sc_dev));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_del_mcast(struct ti_softc *sc, struct ether_addr *addr)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
u_int16_t *m;
|
|
|
|
u_int32_t ext[2] = {0, 0};
|
|
|
|
|
|
|
|
m = (u_int16_t *)&addr->ether_addr_octet[0]; /* XXX */
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (sc->ti_hwrev) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case TI_HWREV_TIGON:
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_MAR0, htons(m[0]));
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_MAR1, (htons(m[1]) << 16) | htons(m[2]));
|
|
|
|
TI_DO_CMD(TI_CMD_DEL_MCAST_ADDR, 0, 0);
|
|
|
|
break;
|
|
|
|
case TI_HWREV_TIGON_II:
|
|
|
|
ext[0] = htons(m[0]);
|
|
|
|
ext[1] = (htons(m[1]) << 16) | htons(m[2]);
|
2007-03-04 08:59:00 +03:00
|
|
|
TI_DO_CMD_EXT(TI_CMD_EXT_DEL_MCAST, 0, 0, (void *)&ext, 2);
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
default:
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: unknown hwrev\n", device_xname(&sc->sc_dev));
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configure the Tigon's multicast address filter.
|
|
|
|
*
|
|
|
|
* The actual multicast table management is a bit of a pain, thanks to
|
|
|
|
* slight brain damage on the part of both Alteon and us. With our
|
|
|
|
* multicast code, we are only alerted when the multicast address table
|
|
|
|
* changes and at that point we only have the current list of addresses:
|
|
|
|
* we only know the current state, not the previous state, so we don't
|
|
|
|
* actually know what addresses were removed or added. The firmware has
|
|
|
|
* state, but we can't get our grubby mits on it, and there is no 'delete
|
|
|
|
* all multicast addresses' command. Hence, we have to maintain our own
|
|
|
|
* state so we know what addresses have been programmed into the NIC at
|
|
|
|
* any given time.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_setmulti(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
struct ti_mc_entry *mc;
|
|
|
|
u_int32_t intrs;
|
|
|
|
struct ether_multi *enm;
|
|
|
|
struct ether_multistep step;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/* Disable interrupts. */
|
|
|
|
intrs = CSR_READ_4(sc, TI_MB_HOSTINTR);
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1);
|
|
|
|
|
|
|
|
/* First, zot all the existing filters. */
|
2001-01-29 04:24:42 +03:00
|
|
|
while ((mc = SIMPLEQ_FIRST(&sc->ti_mc_listhead)) != NULL) {
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_del_mcast(sc, &mc->mc_addr);
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->ti_mc_listhead, mc_entries);
|
1999-09-01 15:47:46 +04:00
|
|
|
free(mc, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
2001-01-29 04:24:42 +03:00
|
|
|
/*
|
|
|
|
* Remember all multicast addresses so that we can delete them
|
|
|
|
* later. Punt if there is a range of addresses or memory shortage.
|
|
|
|
*/
|
1999-09-01 15:47:46 +04:00
|
|
|
ETHER_FIRST_MULTI(step, &sc->ethercom, enm);
|
|
|
|
while (enm != NULL) {
|
2001-01-29 04:24:42 +03:00
|
|
|
if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
|
|
|
|
ETHER_ADDR_LEN) != 0)
|
|
|
|
goto allmulti;
|
|
|
|
if ((mc = malloc(sizeof(struct ti_mc_entry), M_DEVBUF,
|
|
|
|
M_NOWAIT)) == NULL)
|
|
|
|
goto allmulti;
|
|
|
|
memcpy(&mc->mc_addr, enm->enm_addrlo, ETHER_ADDR_LEN);
|
1999-09-01 15:47:46 +04:00
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->ti_mc_listhead, mc, mc_entries);
|
|
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
|
|
}
|
|
|
|
|
2001-01-29 04:24:42 +03:00
|
|
|
/* Accept only programmed multicast addresses */
|
|
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
|
|
|
TI_DO_CMD(TI_CMD_SET_ALLMULTI, TI_CMD_CODE_ALLMULTI_DIS, 0);
|
|
|
|
|
|
|
|
/* Now program new ones. */
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(mc, &sc->ti_mc_listhead, mc_entries)
|
2001-01-29 04:24:42 +03:00
|
|
|
ti_add_mcast(sc, &mc->mc_addr);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/* Re-enable interrupts. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, intrs);
|
|
|
|
|
|
|
|
return;
|
2001-01-29 04:24:42 +03:00
|
|
|
|
|
|
|
allmulti:
|
|
|
|
/* No need to keep individual multicast addresses */
|
|
|
|
while ((mc = SIMPLEQ_FIRST(&sc->ti_mc_listhead)) != NULL) {
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->ti_mc_listhead, mc_entries);
|
2001-01-29 04:24:42 +03:00
|
|
|
free(mc, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Accept all multicast addresses */
|
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
|
|
TI_DO_CMD(TI_CMD_SET_ALLMULTI, TI_CMD_CODE_ALLMULTI_ENB, 0);
|
|
|
|
|
|
|
|
/* Re-enable interrupts. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, intrs);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if the BIOS has configured us for a 64 bit slot when
|
|
|
|
* we aren't actually in one. If we detect this condition, we can work
|
|
|
|
* around it on the Tigon 2 by setting a bit in the PCI state register,
|
|
|
|
* but for the Tigon 1 we must give up and abort the interface attach.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_64bitslot_war(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
if (!(CSR_READ_4(sc, TI_PCI_STATE) & TI_PCISTATE_32BIT_BUS)) {
|
|
|
|
CSR_WRITE_4(sc, 0x600, 0);
|
|
|
|
CSR_WRITE_4(sc, 0x604, 0);
|
|
|
|
CSR_WRITE_4(sc, 0x600, 0x5555AAAA);
|
|
|
|
if (CSR_READ_4(sc, 0x604) == 0x5555AAAA) {
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (EINVAL);
|
1999-09-01 15:47:46 +04:00
|
|
|
else {
|
|
|
|
TI_SETBIT(sc, TI_PCI_STATE,
|
|
|
|
TI_PCISTATE_32BIT_BUS);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do endian, PCI and DMA initialization. Also check the on-board ROM
|
|
|
|
* self-test results.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_chipinit(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
u_int32_t cacheline;
|
|
|
|
u_int32_t pci_writemax = 0;
|
2004-03-19 01:45:35 +03:00
|
|
|
u_int32_t rev;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Initialize link to down state. */
|
|
|
|
sc->ti_linkstat = TI_EV_CODE_LINK_DOWN;
|
|
|
|
|
|
|
|
/* Set endianness before we access any non-PCI registers. */
|
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
CSR_WRITE_4(sc, TI_MISC_HOST_CTL,
|
|
|
|
TI_MHC_BIGENDIAN_INIT | (TI_MHC_BIGENDIAN_INIT << 24));
|
|
|
|
#else
|
|
|
|
CSR_WRITE_4(sc, TI_MISC_HOST_CTL,
|
|
|
|
TI_MHC_LITTLEENDIAN_INIT | (TI_MHC_LITTLEENDIAN_INIT << 24));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Check the ROM failed bit to see if self-tests passed. */
|
|
|
|
if (CSR_READ_4(sc, TI_CPU_STATE) & TI_CPUSTATE_ROMFAIL) {
|
|
|
|
printf("%s: board self-diagnostics failed!\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENODEV);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Halt the CPU. */
|
|
|
|
TI_SETBIT(sc, TI_CPU_STATE, TI_CPUSTATE_HALT);
|
|
|
|
|
|
|
|
/* Figure out the hardware revision. */
|
2004-03-19 01:45:35 +03:00
|
|
|
rev = CSR_READ_4(sc, TI_MISC_HOST_CTL) & TI_MHC_CHIP_REV_MASK;
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (rev) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case TI_REV_TIGON_I:
|
|
|
|
sc->ti_hwrev = TI_HWREV_TIGON;
|
|
|
|
break;
|
|
|
|
case TI_REV_TIGON_II:
|
|
|
|
sc->ti_hwrev = TI_HWREV_TIGON_II;
|
|
|
|
break;
|
|
|
|
default:
|
2004-03-19 01:45:35 +03:00
|
|
|
printf("%s: unsupported chip revision 0x%x\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), rev);
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENODEV);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Do special setup for Tigon 2. */
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON_II) {
|
|
|
|
TI_SETBIT(sc, TI_CPU_CTL_B, TI_CPUSTATE_HALT);
|
|
|
|
TI_SETBIT(sc, TI_MISC_LOCAL_CTL, TI_MLC_SRAM_BANK_256K);
|
|
|
|
TI_SETBIT(sc, TI_MISC_CONF, TI_MCR_SRAM_SYNCHRONOUS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up the PCI state register. */
|
|
|
|
CSR_WRITE_4(sc, TI_PCI_STATE, TI_PCI_READ_CMD|TI_PCI_WRITE_CMD);
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON_II) {
|
|
|
|
TI_SETBIT(sc, TI_PCI_STATE, TI_PCISTATE_USE_MEM_RD_MULT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the read/write max DMA parameters. */
|
|
|
|
TI_CLRBIT(sc, TI_PCI_STATE, (TI_PCISTATE_WRITE_MAXDMA|
|
|
|
|
TI_PCISTATE_READ_MAXDMA));
|
|
|
|
|
|
|
|
/* Get cache line size. */
|
|
|
|
cacheline = PCI_CACHELINE(CSR_READ_4(sc, PCI_BHLC_REG));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the system has set enabled the PCI memory write
|
|
|
|
* and invalidate command in the command register, set
|
|
|
|
* the write max parameter accordingly. This is necessary
|
|
|
|
* to use MWI with the Tigon 2.
|
|
|
|
*/
|
|
|
|
if (CSR_READ_4(sc, PCI_COMMAND_STATUS_REG)
|
|
|
|
& PCI_COMMAND_INVALIDATE_ENABLE) {
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (cacheline) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case 1:
|
|
|
|
case 4:
|
|
|
|
case 8:
|
|
|
|
case 16:
|
|
|
|
case 32:
|
|
|
|
case 64:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Disable PCI memory write and invalidate. */
|
|
|
|
if (bootverbose)
|
|
|
|
printf("%s: cache line size %d not "
|
|
|
|
"supported; disabling PCI MWI\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), cacheline);
|
1999-09-01 15:47:46 +04:00
|
|
|
CSR_WRITE_4(sc, PCI_COMMAND_STATUS_REG,
|
|
|
|
CSR_READ_4(sc, PCI_COMMAND_STATUS_REG)
|
|
|
|
& ~PCI_COMMAND_INVALIDATE_ENABLE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __brokenalpha__
|
|
|
|
/*
|
|
|
|
* From the Alteon sample driver:
|
|
|
|
* Must insure that we do not cross an 8K (bytes) boundary
|
2005-02-27 03:26:58 +03:00
|
|
|
* for DMA reads. Our highest limit is 1K bytes. This is a
|
|
|
|
* restriction on some ALPHA platforms with early revision
|
|
|
|
* 21174 PCI chipsets, such as the AlphaPC 164lx
|
1999-09-01 15:47:46 +04:00
|
|
|
*/
|
|
|
|
TI_SETBIT(sc, TI_PCI_STATE, pci_writemax|TI_PCI_READMAX_1024);
|
|
|
|
#else
|
|
|
|
TI_SETBIT(sc, TI_PCI_STATE, pci_writemax);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This sets the min dma param all the way up (0xff). */
|
|
|
|
TI_SETBIT(sc, TI_PCI_STATE, TI_PCISTATE_MINDMA);
|
|
|
|
|
|
|
|
/* Configure DMA variables. */
|
|
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_OPMODE, TI_OPMODE_BYTESWAP_BD |
|
|
|
|
TI_OPMODE_BYTESWAP_DATA | TI_OPMODE_WORDSWAP_BD |
|
|
|
|
TI_OPMODE_WARN_ENB | TI_OPMODE_FATAL_ENB |
|
|
|
|
TI_OPMODE_DONT_FRAG_JUMBO);
|
|
|
|
#else
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_OPMODE, TI_OPMODE_BYTESWAP_DATA|
|
|
|
|
TI_OPMODE_WORDSWAP_BD|TI_OPMODE_DONT_FRAG_JUMBO|
|
|
|
|
TI_OPMODE_WARN_ENB|TI_OPMODE_FATAL_ENB);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only allow 1 DMA channel to be active at a time.
|
|
|
|
* I don't think this is a good idea, but without it
|
|
|
|
* the firmware racks up lots of nicDmaReadRingFull
|
|
|
|
* errors.
|
2001-06-07 18:35:58 +04:00
|
|
|
* Incompatible with hardware assisted checksums.
|
1999-09-01 15:47:46 +04:00
|
|
|
*/
|
2001-06-07 18:35:58 +04:00
|
|
|
if ((sc->ethercom.ec_if.if_capenable &
|
2005-05-02 19:34:31 +04:00
|
|
|
(IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
|
|
|
|
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
|
|
|
|
IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx)) == 0)
|
2001-06-07 18:35:58 +04:00
|
|
|
TI_SETBIT(sc, TI_GCR_OPMODE, TI_OPMODE_1_DMA_ACTIVE);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Recommended settings from Tigon manual. */
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_DMA_WRITECFG, TI_DMA_STATE_THRESH_8W);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_DMA_READCFG, TI_DMA_STATE_THRESH_8W);
|
|
|
|
|
|
|
|
if (ti_64bitslot_war(sc)) {
|
|
|
|
printf("%s: bios thinks we're in a 64 bit slot, "
|
2008-04-10 23:13:36 +04:00
|
|
|
"but we aren't", device_xname(&sc->sc_dev));
|
2007-09-08 03:05:27 +04:00
|
|
|
return (EINVAL);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the general information block and firmware, and
|
|
|
|
* start the CPU(s) running.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_gibinit(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_rcb *rcb;
|
|
|
|
int i;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/* Disable interrupts for now. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1);
|
|
|
|
|
|
|
|
/* Tell the chip where to find the general information block. */
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_GENINFO_HI, 0);
|
2001-06-30 19:39:51 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_GENINFO_LO, TI_CDGIBADDR(sc));
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Load the firmware into SRAM. */
|
|
|
|
ti_loadfw(sc);
|
|
|
|
|
|
|
|
/* Set up the contents of the general info and ring control blocks. */
|
|
|
|
|
|
|
|
/* Set up the event ring and producer pointer. */
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_ev_rcb;
|
|
|
|
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDEVENTADDR(sc, 0);
|
1999-09-01 15:47:46 +04:00
|
|
|
rcb->ti_flags = 0;
|
|
|
|
TI_HOSTADDR(sc->ti_rdata->ti_info.ti_ev_prodidx_ptr) =
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_CDEVPRODADDR(sc);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
sc->ti_ev_prodidx.ti_idx = 0;
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_EVENTCONS_IDX, 0);
|
|
|
|
sc->ti_ev_saved_considx = 0;
|
|
|
|
|
|
|
|
/* Set up the command ring and producer mailbox. */
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_cmd_rcb;
|
|
|
|
|
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_GCR_NIC_ADDR(TI_GCR_CMDRING);
|
|
|
|
rcb->ti_flags = 0;
|
|
|
|
rcb->ti_max_len = 0;
|
|
|
|
for (i = 0; i < TI_CMD_RING_CNT; i++) {
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_CMDRING + (i * 4), 0);
|
|
|
|
}
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_CMDCONS_IDX, 0);
|
|
|
|
CSR_WRITE_4(sc, TI_MB_CMDPROD_IDX, 0);
|
|
|
|
sc->ti_cmd_saved_prodidx = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assign the address of the stats refresh buffer.
|
|
|
|
* We re-use the current stats buffer for this to
|
|
|
|
* conserve memory.
|
|
|
|
*/
|
|
|
|
TI_HOSTADDR(sc->ti_rdata->ti_info.ti_refresh_stats_ptr) =
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_CDSTATSADDR(sc);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Set up the standard receive ring. */
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_std_rx_rcb;
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDRXSTDADDR(sc, 0);
|
2001-06-03 07:29:44 +04:00
|
|
|
rcb->ti_max_len = ETHER_MAX_LEN;
|
1999-09-01 15:47:46 +04:00
|
|
|
rcb->ti_flags = 0;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_IP_CKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM;
|
2005-02-20 18:48:35 +03:00
|
|
|
if (VLAN_ATTACHED(&sc->ethercom))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Set up the jumbo receive ring. */
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_jumbo_rx_rcb;
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDRXJUMBOADDR(sc, 0);
|
2001-06-03 07:29:44 +04:00
|
|
|
rcb->ti_max_len = ETHER_MAX_LEN_JUMBO;
|
1999-09-01 15:47:46 +04:00
|
|
|
rcb->ti_flags = 0;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_IP_CKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM;
|
2005-02-20 18:48:35 +03:00
|
|
|
if (VLAN_ATTACHED(&sc->ethercom))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the mini ring. Only activated on the
|
|
|
|
* Tigon 2 but the slot in the config block is
|
|
|
|
* still there on the Tigon 1.
|
|
|
|
*/
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_mini_rx_rcb;
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDRXMINIADDR(sc, 0);
|
1999-10-06 15:23:33 +04:00
|
|
|
rcb->ti_max_len = MHLEN - ETHER_ALIGN;
|
1999-09-01 15:47:46 +04:00
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON)
|
|
|
|
rcb->ti_flags = TI_RCB_FLAG_RING_DISABLED;
|
|
|
|
else
|
|
|
|
rcb->ti_flags = 0;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & IFCAP_CSUM_IPv4_Rx)
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_IP_CKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM;
|
2005-02-20 18:48:35 +03:00
|
|
|
if (VLAN_ATTACHED(&sc->ethercom))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the receive return ring.
|
|
|
|
*/
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_return_rcb;
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDRXRTNADDR(sc, 0);
|
1999-09-01 15:47:46 +04:00
|
|
|
rcb->ti_flags = 0;
|
|
|
|
rcb->ti_max_len = TI_RETURN_RING_CNT;
|
|
|
|
TI_HOSTADDR(sc->ti_rdata->ti_info.ti_return_prodidx_ptr) =
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_CDRTNPRODADDR(sc);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the tx ring. Note: for the Tigon 2, we have the option
|
|
|
|
* of putting the transmit ring in the host's address space and
|
|
|
|
* letting the chip DMA it instead of leaving the ring in the NIC's
|
|
|
|
* memory and accessing it through the shared memory region. We
|
|
|
|
* do this for the Tigon 2, but it doesn't work on the Tigon 1,
|
|
|
|
* so we have to revert to the shared memory scheme if we detect
|
|
|
|
* a Tigon 1 chip.
|
|
|
|
*/
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE, TI_TX_RING_BASE);
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON) {
|
2001-06-30 18:16:55 +04:00
|
|
|
sc->ti_tx_ring_nic =
|
1999-09-01 15:47:46 +04:00
|
|
|
(struct ti_tx_desc *)(sc->ti_vhandle + TI_WINDOW);
|
|
|
|
}
|
2001-07-07 20:46:34 +04:00
|
|
|
memset((char *)sc->ti_rdata->ti_tx_ring, 0,
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_TX_RING_CNT * sizeof(struct ti_tx_desc));
|
|
|
|
rcb = &sc->ti_rdata->ti_info.ti_tx_rcb;
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON)
|
|
|
|
rcb->ti_flags = 0;
|
|
|
|
else
|
|
|
|
rcb->ti_flags = TI_RCB_FLAG_HOST_RING;
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & IFCAP_CSUM_IPv4_Tx)
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_IP_CKSUM;
|
|
|
|
/*
|
|
|
|
* When we get the packet, there is a pseudo-header seed already
|
|
|
|
* in the th_sum or uh_sum field. Make sure the firmware doesn't
|
|
|
|
* compute the pseudo-header checksum again!
|
|
|
|
*/
|
2005-05-02 19:34:31 +04:00
|
|
|
if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_UDPv4_Tx))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM|
|
|
|
|
TI_RCB_FLAG_NO_PHDR_CKSUM;
|
2005-02-20 18:48:35 +03:00
|
|
|
if (VLAN_ATTACHED(&sc->ethercom))
|
2001-06-02 20:17:06 +04:00
|
|
|
rcb->ti_flags |= TI_RCB_FLAG_VLAN_ASSIST;
|
1999-09-01 15:47:46 +04:00
|
|
|
rcb->ti_max_len = TI_TX_RING_CNT;
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON)
|
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_TX_RING_BASE;
|
|
|
|
else
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_HOSTADDR(rcb->ti_hostaddr) = TI_CDTXADDR(sc, 0);
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_HOSTADDR(sc->ti_rdata->ti_info.ti_tx_considx_ptr) =
|
2001-06-30 19:39:51 +04:00
|
|
|
TI_CDTXCONSADDR(sc);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2001-06-30 20:34:59 +04:00
|
|
|
/*
|
|
|
|
* We're done frobbing the General Information Block. Sync
|
|
|
|
* it. Note we take care of the first stats sync here, as
|
|
|
|
* well.
|
|
|
|
*/
|
|
|
|
TI_CDGIBSYNC(sc, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/* Set up tuneables */
|
2000-11-12 21:32:43 +03:00
|
|
|
if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN) ||
|
|
|
|
(sc->ethercom.ec_capenable & ETHERCAP_VLAN_MTU))
|
1999-09-01 15:47:46 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS,
|
|
|
|
(sc->ti_rx_coal_ticks / 10));
|
|
|
|
else
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS, sc->ti_rx_coal_ticks);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_TX_COAL_TICKS, sc->ti_tx_coal_ticks);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_STAT_TICKS, sc->ti_stat_ticks);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_RX_MAX_COAL_BD, sc->ti_rx_max_coal_bds);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_TX_MAX_COAL_BD, sc->ti_tx_max_coal_bds);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_TX_BUFFER_RATIO, sc->ti_tx_buf_ratio);
|
|
|
|
|
|
|
|
/* Turn interrupts on. */
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_MASK_INTRS, 0);
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0);
|
|
|
|
|
|
|
|
/* Start CPU. */
|
|
|
|
TI_CLRBIT(sc, TI_CPU_STATE, (TI_CPUSTATE_HALT|TI_CPUSTATE_STEP));
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2000-03-17 12:20:16 +03:00
|
|
|
/*
|
|
|
|
* look for id in the device list, returning the first match
|
|
|
|
*/
|
2001-01-18 23:28:15 +03:00
|
|
|
static const struct ti_type *
|
2007-09-08 03:05:27 +04:00
|
|
|
ti_type_match(struct pci_attach_args *pa)
|
2000-03-17 12:20:16 +03:00
|
|
|
{
|
2001-01-18 23:28:15 +03:00
|
|
|
const struct ti_type *t;
|
2000-03-17 12:20:16 +03:00
|
|
|
|
|
|
|
t = ti_devs;
|
2007-09-08 03:05:27 +04:00
|
|
|
while (t->ti_name != NULL) {
|
2000-03-17 12:20:16 +03:00
|
|
|
if ((PCI_VENDOR(pa->pa_id) == t->ti_vid) &&
|
|
|
|
(PCI_PRODUCT(pa->pa_id) == t->ti_did)) {
|
|
|
|
return (t);
|
|
|
|
}
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (NULL);
|
2000-03-17 12:20:16 +03:00
|
|
|
}
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/*
|
|
|
|
* Probe for a Tigon chip. Check the PCI vendor and device IDs
|
|
|
|
* against our list and return its name if we find a match.
|
|
|
|
*/
|
2006-10-12 05:30:41 +04:00
|
|
|
static int
|
2009-05-06 14:34:32 +04:00
|
|
|
ti_probe(device_t parent, cfdata_t match, void *aux)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct pci_attach_args *pa = aux;
|
2001-01-18 23:28:15 +03:00
|
|
|
const struct ti_type *t;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2000-03-17 12:20:16 +03:00
|
|
|
t = ti_type_match(pa);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return ((t == NULL) ? 0 : 1);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2006-10-12 05:30:41 +04:00
|
|
|
static void
|
2009-05-06 14:34:32 +04:00
|
|
|
ti_attach(device_t parent, device_t self, void *aux)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
u_int32_t command;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct ti_softc *sc;
|
2007-09-08 03:05:27 +04:00
|
|
|
u_int8_t eaddr[ETHER_ADDR_LEN];
|
1999-09-01 15:47:46 +04:00
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
const char *intrstr = NULL;
|
|
|
|
bus_dma_segment_t dmaseg;
|
2000-03-17 12:20:16 +03:00
|
|
|
int error, dmanseg, nolinear;
|
2001-01-18 23:28:15 +03:00
|
|
|
const struct ti_type *t;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2000-03-17 12:20:16 +03:00
|
|
|
t = ti_type_match(pa);
|
|
|
|
if (t == NULL) {
|
|
|
|
printf("ti_attach: were did the card go ?\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(": %s (rev. 0x%02x)\n", t->ti_name, PCI_REVISION(pa->pa_class));
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2009-05-12 12:22:59 +04:00
|
|
|
sc = device_private(self);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Map control/status registers.
|
|
|
|
*/
|
2000-03-17 12:20:16 +03:00
|
|
|
nolinear = 0;
|
|
|
|
if (pci_mapreg_map(pa, 0x10,
|
|
|
|
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
|
|
|
|
BUS_SPACE_MAP_LINEAR , &sc->ti_btag, &sc->ti_bhandle,
|
|
|
|
NULL, NULL)) {
|
|
|
|
nolinear = 1;
|
|
|
|
if (pci_mapreg_map(pa, 0x10,
|
|
|
|
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
|
|
|
|
0 , &sc->ti_btag, &sc->ti_bhandle, NULL, NULL)) {
|
|
|
|
printf(": can't map memory space\n");
|
|
|
|
return;
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
2000-03-17 12:20:16 +03:00
|
|
|
if (nolinear == 0)
|
2002-03-20 20:32:23 +03:00
|
|
|
sc->ti_vhandle = bus_space_vaddr(sc->ti_btag, sc->ti_bhandle);
|
2005-02-27 03:26:58 +03:00
|
|
|
else
|
2000-03-17 12:20:16 +03:00
|
|
|
sc->ti_vhandle = NULL;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
command |= PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command);
|
|
|
|
|
|
|
|
/* Allocate interrupt */
|
2000-12-29 01:59:06 +03:00
|
|
|
if (pci_intr_map(pa, &ih)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "couldn't map interrupt\n");
|
2003-01-20 08:29:53 +03:00
|
|
|
return;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
intrstr = pci_intr_string(pc, ih);
|
|
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, ti_intr, sc);
|
|
|
|
if (sc->sc_ih == NULL) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "couldn't establish interrupt");
|
1999-09-01 15:47:46 +04:00
|
|
|
if (intrstr != NULL)
|
2009-11-26 18:17:08 +03:00
|
|
|
aprint_error(" at %s", intrstr);
|
|
|
|
aprint_error("\n");
|
2003-01-20 08:29:53 +03:00
|
|
|
return;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
2009-11-26 18:17:08 +03:00
|
|
|
aprint_normal_dev(&sc->sc_dev, "interrupting at %s\n", intrstr);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
if (ti_chipinit(sc)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "chip initialization failed\n");
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
|
|
|
}
|
2001-06-30 18:47:23 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Deal with some chip diffrences.
|
|
|
|
*/
|
|
|
|
switch (sc->ti_hwrev) {
|
|
|
|
case TI_HWREV_TIGON:
|
|
|
|
sc->sc_tx_encap = ti_encap_tigon1;
|
2001-06-30 18:56:59 +04:00
|
|
|
sc->sc_tx_eof = ti_txeof_tigon1;
|
2001-06-30 18:47:23 +04:00
|
|
|
if (nolinear == 1)
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "memory space not mapped linear\n");
|
2001-06-30 18:47:23 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TI_HWREV_TIGON_II:
|
|
|
|
sc->sc_tx_encap = ti_encap_tigon2;
|
2001-06-30 18:56:59 +04:00
|
|
|
sc->sc_tx_eof = ti_txeof_tigon2;
|
2001-06-30 18:47:23 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: Unknown chip version: %d\n", device_xname(self),
|
2001-06-30 18:47:23 +04:00
|
|
|
sc->ti_hwrev);
|
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Zero out the NIC's on-board SRAM. */
|
|
|
|
ti_mem(sc, 0x2000, 0x100000 - 0x2000, NULL);
|
|
|
|
|
|
|
|
/* Init again -- zeroing memory may have clobbered some registers. */
|
|
|
|
if (ti_chipinit(sc)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "chip initialization failed\n");
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get station address from the EEPROM. Note: the manual states
|
|
|
|
* that the MAC address is at offset 0x8c, however the data is
|
|
|
|
* stored as two longwords (since that's how it's loaded into
|
2001-08-20 16:00:46 +04:00
|
|
|
* the NIC). This means the MAC address is actually preceded
|
1999-09-01 15:47:46 +04:00
|
|
|
* by two zero bytes. We need to skip over those.
|
|
|
|
*/
|
2007-03-04 08:59:00 +03:00
|
|
|
if (ti_read_eeprom(sc, (void *)&eaddr,
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_EE_MAC_OFFSET + 2, ETHER_ADDR_LEN)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "failed to read station address\n");
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A Tigon chip was detected. Inform the world.
|
|
|
|
*/
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "Ethernet address: %s\n",
|
1999-09-01 15:47:46 +04:00
|
|
|
ether_sprintf(eaddr));
|
|
|
|
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
|
|
|
|
/* Allocate the general information block and ring buffers. */
|
|
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat,
|
2000-11-14 21:42:55 +03:00
|
|
|
sizeof(struct ti_ring_data), PAGE_SIZE, 0, &dmaseg, 1, &dmanseg,
|
1999-09-01 15:47:46 +04:00
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't allocate ring buffer, error = %d\n",
|
|
|
|
error);
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &dmaseg, dmanseg,
|
2007-03-04 08:59:00 +03:00
|
|
|
sizeof(struct ti_ring_data), (void **)&sc->ti_rdata,
|
1999-09-01 15:47:46 +04:00
|
|
|
BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't map ring buffer, error = %d\n",
|
|
|
|
error);
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat,
|
|
|
|
sizeof(struct ti_ring_data), 1,
|
|
|
|
sizeof(struct ti_ring_data), 0, BUS_DMA_NOWAIT,
|
|
|
|
&sc->info_dmamap)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't create ring buffer DMA map, error = %d\n",
|
|
|
|
error);
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->info_dmamap,
|
|
|
|
sc->ti_rdata, sizeof(struct ti_ring_data), NULL,
|
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "can't load ring buffer DMA map, error = %d\n",
|
|
|
|
error);
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->info_dmaaddr = sc->info_dmamap->dm_segs[0].ds_addr;
|
|
|
|
|
2001-07-07 20:46:34 +04:00
|
|
|
memset(sc->ti_rdata, 0, sizeof(struct ti_ring_data));
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Try to allocate memory for jumbo buffers. */
|
|
|
|
if (ti_alloc_jumbo_mem(sc)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(self, "jumbo buffer allocation failed\n");
|
2000-03-17 12:20:16 +03:00
|
|
|
goto fail2;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2001-01-29 04:24:42 +03:00
|
|
|
SIMPLEQ_INIT(&sc->ti_mc_listhead);
|
|
|
|
|
2000-11-17 22:33:25 +03:00
|
|
|
/*
|
2001-06-30 21:53:58 +04:00
|
|
|
* We really need a better way to tell a 1000baseT card
|
2000-11-17 22:33:25 +03:00
|
|
|
* from a 1000baseSX one, since in theory there could be
|
2001-06-30 21:53:58 +04:00
|
|
|
* OEMed 1000baseT cards from lame vendors who aren't
|
2000-11-17 22:33:25 +03:00
|
|
|
* clever enough to change the PCI ID. For the moment
|
|
|
|
* though, the AceNIC is the only copper card available.
|
|
|
|
*/
|
|
|
|
if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALTEON &&
|
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALTEON_ACENIC_COPPER) ||
|
|
|
|
(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NETGEAR &&
|
|
|
|
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NETGEAR_GA620T))
|
|
|
|
sc->ti_copper = 1;
|
|
|
|
else
|
|
|
|
sc->ti_copper = 0;
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/* Set default tuneable values. */
|
|
|
|
sc->ti_stat_ticks = 2 * TI_TICKS_PER_SEC;
|
|
|
|
sc->ti_rx_coal_ticks = TI_TICKS_PER_SEC / 5000;
|
|
|
|
sc->ti_tx_coal_ticks = TI_TICKS_PER_SEC / 500;
|
|
|
|
sc->ti_rx_max_coal_bds = 64;
|
|
|
|
sc->ti_tx_max_coal_bds = 128;
|
|
|
|
sc->ti_tx_buf_ratio = 21;
|
|
|
|
|
|
|
|
/* Set up ifnet structure */
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
ifp->if_softc = sc;
|
2008-04-10 23:13:36 +04:00
|
|
|
strlcpy(ifp->if_xname, device_xname(&sc->sc_dev), IFNAMSIZ);
|
1999-09-01 15:47:46 +04:00
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_ioctl = ti_ioctl;
|
|
|
|
ifp->if_start = ti_start;
|
|
|
|
ifp->if_watchdog = ti_watchdog;
|
2000-12-14 09:42:57 +03:00
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* XXX This is not really correct -- we don't necessarily
|
|
|
|
* XXX want to queue up as many as we can transmit at the
|
|
|
|
* XXX upper layer like that. Someone with a board should
|
|
|
|
* XXX check to see how this affects performance.
|
|
|
|
*/
|
1999-09-01 15:47:46 +04:00
|
|
|
ifp->if_snd.ifq_maxlen = TI_TX_RING_CNT - 1;
|
2000-12-14 09:42:57 +03:00
|
|
|
#endif
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2000-11-12 21:32:43 +03:00
|
|
|
/*
|
|
|
|
* We can support 802.1Q VLAN-sized frames.
|
|
|
|
*/
|
2000-11-17 22:33:25 +03:00
|
|
|
sc->ethercom.ec_capabilities |=
|
|
|
|
ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING;
|
2000-11-12 21:32:43 +03:00
|
|
|
|
2001-06-02 20:17:06 +04:00
|
|
|
/*
|
|
|
|
* We can do IPv4, TCPv4, and UDPv4 checksums in hardware.
|
|
|
|
*/
|
2005-05-02 19:34:31 +04:00
|
|
|
ifp->if_capabilities |=
|
|
|
|
IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
|
|
|
|
IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
|
|
|
|
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx;
|
2001-06-02 20:17:06 +04:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/* Set up ifmedia support. */
|
|
|
|
ifmedia_init(&sc->ifmedia, IFM_IMASK, ti_ifmedia_upd, ti_ifmedia_sts);
|
2000-11-17 22:33:25 +03:00
|
|
|
if (sc->ti_copper) {
|
|
|
|
/*
|
|
|
|
* Copper cards allow manual 10/100 mode selection,
|
2001-06-30 21:53:58 +04:00
|
|
|
* but not manual 1000baseT mode selection. Why?
|
2004-02-24 18:05:53 +03:00
|
|
|
* Because currently there's no way to specify the
|
2000-11-17 22:33:25 +03:00
|
|
|
* master/slave setting through the firmware interface,
|
|
|
|
* so Alteon decided to just bag it and handle it
|
|
|
|
* via autonegotiation.
|
|
|
|
*/
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia,
|
|
|
|
IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia,
|
|
|
|
IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
|
2001-06-30 21:53:58 +04:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_1000_T, 0, NULL);
|
2000-11-17 22:33:25 +03:00
|
|
|
ifmedia_add(&sc->ifmedia,
|
2001-06-30 21:53:58 +04:00
|
|
|
IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
|
2000-11-17 22:33:25 +03:00
|
|
|
} else {
|
|
|
|
/* Fiber cards don't support 10/100 modes. */
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_1000_SX, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_1000_SX|IFM_FDX, 0, NULL);
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
|
|
|
|
ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call MI attach routines.
|
|
|
|
*/
|
|
|
|
if_attach(ifp);
|
|
|
|
ether_ifattach(ifp, eaddr);
|
|
|
|
|
2009-09-27 16:52:59 +04:00
|
|
|
/*
|
|
|
|
* Add shutdown hook so that DMA is disabled prior to reboot. Not
|
|
|
|
* doing do could allow DMA to corrupt kernel memory during the
|
|
|
|
* reboot before the driver initializes.
|
|
|
|
*/
|
|
|
|
if (pmf_device_register1(self, NULL, NULL, ti_shutdown))
|
|
|
|
pmf_class_network_register(self, ifp);
|
|
|
|
else
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
|
2000-03-17 12:20:16 +03:00
|
|
|
return;
|
|
|
|
fail2:
|
|
|
|
pci_intr_disestablish(pc, sc->sc_ih);
|
|
|
|
return;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frame reception handling. This is called if there's a frame
|
|
|
|
* on the receive return list.
|
|
|
|
*
|
|
|
|
* Note: we have to be able to handle three possibilities here:
|
|
|
|
* 1) the frame is from the mini receive ring (can only happen)
|
|
|
|
* on Tigon 2 boards)
|
2001-06-12 19:17:10 +04:00
|
|
|
* 2) the frame is from the jumbo receive ring
|
1999-09-01 15:47:46 +04:00
|
|
|
* 3) the frame is from the standard receive ring
|
|
|
|
*/
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_rxeof(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
while (sc->ti_rx_saved_considx != sc->ti_return_prodidx.ti_idx) {
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ti_rx_desc *cur_rx;
|
|
|
|
u_int32_t rxidx;
|
|
|
|
struct mbuf *m = NULL;
|
2001-06-02 20:17:06 +04:00
|
|
|
struct ether_header *eh;
|
1999-09-01 15:47:46 +04:00
|
|
|
bus_dmamap_t dmamap;
|
|
|
|
|
|
|
|
cur_rx =
|
|
|
|
&sc->ti_rdata->ti_rx_return_ring[sc->ti_rx_saved_considx];
|
|
|
|
rxidx = cur_rx->ti_idx;
|
|
|
|
TI_INC(sc->ti_rx_saved_considx, TI_RETURN_RING_CNT);
|
|
|
|
|
|
|
|
if (cur_rx->ti_flags & TI_BDFLAG_JUMBO_RING) {
|
|
|
|
TI_INC(sc->ti_jumbo, TI_JUMBO_RX_RING_CNT);
|
|
|
|
m = sc->ti_cdata.ti_rx_jumbo_chain[rxidx];
|
|
|
|
sc->ti_cdata.ti_rx_jumbo_chain[rxidx] = NULL;
|
|
|
|
if (cur_rx->ti_flags & TI_BDFLAG_ERROR) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_jumbo(sc, sc->ti_jumbo, m);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ti_newbuf_jumbo(sc, sc->ti_jumbo, NULL)
|
|
|
|
== ENOBUFS) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_jumbo(sc, sc->ti_jumbo, m);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (cur_rx->ti_flags & TI_BDFLAG_MINI_RING) {
|
|
|
|
TI_INC(sc->ti_mini, TI_MINI_RX_RING_CNT);
|
|
|
|
m = sc->ti_cdata.ti_rx_mini_chain[rxidx];
|
|
|
|
sc->ti_cdata.ti_rx_mini_chain[rxidx] = NULL;
|
|
|
|
dmamap = sc->mini_dmamap[rxidx];
|
|
|
|
sc->mini_dmamap[rxidx] = 0;
|
|
|
|
if (cur_rx->ti_flags & TI_BDFLAG_ERROR) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_mini(sc, sc->ti_mini, m, dmamap);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ti_newbuf_mini(sc, sc->ti_mini, NULL, dmamap)
|
|
|
|
== ENOBUFS) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_mini(sc, sc->ti_mini, m, dmamap);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TI_INC(sc->ti_std, TI_STD_RX_RING_CNT);
|
|
|
|
m = sc->ti_cdata.ti_rx_std_chain[rxidx];
|
|
|
|
sc->ti_cdata.ti_rx_std_chain[rxidx] = NULL;
|
|
|
|
dmamap = sc->std_dmamap[rxidx];
|
|
|
|
sc->std_dmamap[rxidx] = 0;
|
|
|
|
if (cur_rx->ti_flags & TI_BDFLAG_ERROR) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_std(sc, sc->ti_std, m, dmamap);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ti_newbuf_std(sc, sc->ti_std, NULL, dmamap)
|
|
|
|
== ENOBUFS) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
ti_newbuf_std(sc, sc->ti_std, m, dmamap);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m->m_pkthdr.len = m->m_len = cur_rx->ti_len;
|
|
|
|
ifp->if_ipackets++;
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle BPF listeners. Let the BPF user see the packet, but
|
|
|
|
* don't pass it up to the ether_input() layer unless it's
|
|
|
|
* a broadcast packet, multicast packet, matches our ethernet
|
|
|
|
* address or the interface is in promiscuous mode.
|
|
|
|
*/
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2001-06-02 20:17:06 +04:00
|
|
|
eh = mtod(m, struct ether_header *);
|
|
|
|
switch (ntohs(eh->ether_type)) {
|
2001-11-26 05:41:20 +03:00
|
|
|
#ifdef INET
|
2001-06-02 20:17:06 +04:00
|
|
|
case ETHERTYPE_IP:
|
|
|
|
{
|
|
|
|
struct ip *ip = (struct ip *) (eh + 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note the Tigon firmware does not invert
|
|
|
|
* the checksum for us, hence the XOR.
|
|
|
|
*/
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
|
|
|
|
if ((cur_rx->ti_ip_cksum ^ 0xffff) != 0)
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
|
|
|
|
/*
|
|
|
|
* ntohs() the constant so the compiler can
|
|
|
|
* optimize...
|
|
|
|
*
|
|
|
|
* XXX Figure out a sane way to deal with
|
|
|
|
* fragmented packets.
|
|
|
|
*/
|
|
|
|
if ((ip->ip_off & htons(IP_MF|IP_OFFMASK)) == 0) {
|
|
|
|
switch (ip->ip_p) {
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
m->m_pkthdr.csum_data =
|
|
|
|
cur_rx->ti_tcp_udp_cksum;
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
M_CSUM_TCPv4|M_CSUM_DATA;
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
m->m_pkthdr.csum_data =
|
|
|
|
cur_rx->ti_tcp_udp_cksum;
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
M_CSUM_UDPv4|M_CSUM_DATA;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Nothing */;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2001-11-26 05:41:20 +03:00
|
|
|
#endif
|
2001-06-02 20:17:06 +04:00
|
|
|
default:
|
|
|
|
/* Nothing. */
|
|
|
|
break;
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2005-02-20 18:48:35 +03:00
|
|
|
if (cur_rx->ti_flags & TI_BDFLAG_VLAN_TAG) {
|
|
|
|
VLAN_INPUT_TAG(ifp, m,
|
|
|
|
/* ti_vlan_tag also has the priority, trim it */
|
|
|
|
cur_rx->ti_vlan_tag & 4095,
|
|
|
|
continue);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
2005-02-20 18:48:35 +03:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
(*ifp->if_input)(ifp, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only necessary on the Tigon 1. */
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON)
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_RXRETURNCONS_IDX,
|
|
|
|
sc->ti_rx_saved_considx);
|
|
|
|
|
|
|
|
TI_UPDATE_STDPROD(sc, sc->ti_std);
|
|
|
|
TI_UPDATE_MINIPROD(sc, sc->ti_mini);
|
|
|
|
TI_UPDATE_JUMBOPROD(sc, sc->ti_jumbo);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_txeof_tigon1(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_tx_desc *cur_tx = NULL;
|
|
|
|
struct ifnet *ifp;
|
2001-06-30 09:48:24 +04:00
|
|
|
struct txdmamap_pool_entry *dma;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through our tx ring and free mbufs for those
|
|
|
|
* frames that have been sent.
|
|
|
|
*/
|
|
|
|
while (sc->ti_tx_saved_considx != sc->ti_tx_considx.ti_idx) {
|
|
|
|
u_int32_t idx = 0;
|
|
|
|
|
|
|
|
idx = sc->ti_tx_saved_considx;
|
2001-06-30 18:56:59 +04:00
|
|
|
if (idx > 383)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 6144);
|
|
|
|
else if (idx > 255)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 4096);
|
|
|
|
else if (idx > 127)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 2048);
|
|
|
|
else
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE);
|
|
|
|
cur_tx = &sc->ti_tx_ring_nic[idx % 128];
|
|
|
|
if (cur_tx->ti_flags & TI_BDFLAG_END)
|
|
|
|
ifp->if_opackets++;
|
|
|
|
if (sc->ti_cdata.ti_tx_chain[idx] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_tx_chain[idx]);
|
|
|
|
sc->ti_cdata.ti_tx_chain[idx] = NULL;
|
|
|
|
|
|
|
|
dma = sc->txdma[idx];
|
|
|
|
KDASSERT(dma != NULL);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma->dmamap, 0,
|
|
|
|
dma->dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, dma->dmamap);
|
|
|
|
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->txdma_list, dma, link);
|
|
|
|
sc->txdma[idx] = NULL;
|
|
|
|
}
|
|
|
|
sc->ti_txcnt--;
|
|
|
|
TI_INC(sc->ti_tx_saved_considx, TI_TX_RING_CNT);
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur_tx != NULL)
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_txeof_tigon2(struct ti_softc *sc)
|
2001-06-30 18:56:59 +04:00
|
|
|
{
|
|
|
|
struct ti_tx_desc *cur_tx = NULL;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct txdmamap_pool_entry *dma;
|
2001-06-30 21:02:54 +04:00
|
|
|
int firstidx, cnt;
|
2001-06-30 18:56:59 +04:00
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through our tx ring and free mbufs for those
|
|
|
|
* frames that have been sent.
|
|
|
|
*/
|
2001-06-30 21:02:54 +04:00
|
|
|
firstidx = sc->ti_tx_saved_considx;
|
|
|
|
cnt = 0;
|
2001-06-30 18:56:59 +04:00
|
|
|
while (sc->ti_tx_saved_considx != sc->ti_tx_considx.ti_idx) {
|
|
|
|
u_int32_t idx = 0;
|
|
|
|
|
|
|
|
idx = sc->ti_tx_saved_considx;
|
|
|
|
cur_tx = &sc->ti_rdata->ti_tx_ring[idx];
|
1999-09-01 15:47:46 +04:00
|
|
|
if (cur_tx->ti_flags & TI_BDFLAG_END)
|
|
|
|
ifp->if_opackets++;
|
|
|
|
if (sc->ti_cdata.ti_tx_chain[idx] != NULL) {
|
|
|
|
m_freem(sc->ti_cdata.ti_tx_chain[idx]);
|
|
|
|
sc->ti_cdata.ti_tx_chain[idx] = NULL;
|
|
|
|
|
2001-06-30 09:48:24 +04:00
|
|
|
dma = sc->txdma[idx];
|
|
|
|
KDASSERT(dma != NULL);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma->dmamap, 0,
|
|
|
|
dma->dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, dma->dmamap);
|
|
|
|
|
|
|
|
SIMPLEQ_INSERT_HEAD(&sc->txdma_list, dma, link);
|
|
|
|
sc->txdma[idx] = NULL;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
2001-06-30 21:02:54 +04:00
|
|
|
cnt++;
|
1999-09-01 15:47:46 +04:00
|
|
|
sc->ti_txcnt--;
|
|
|
|
TI_INC(sc->ti_tx_saved_considx, TI_TX_RING_CNT);
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
}
|
|
|
|
|
2001-06-30 21:02:54 +04:00
|
|
|
if (cnt != 0)
|
|
|
|
TI_CDTXSYNC(sc, firstidx, cnt, BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
if (cur_tx != NULL)
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_intr(void *xsc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
sc = xsc;
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
#ifdef notdef
|
|
|
|
/* Avoid this for now -- checking this register is expensive. */
|
|
|
|
/* Make sure this is really our interrupt. */
|
|
|
|
if (!(CSR_READ_4(sc, TI_MISC_HOST_CTL) & TI_MHC_INTSTATE))
|
|
|
|
return (0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Ack interrupt and stop others from occuring. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1);
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_RUNNING) {
|
|
|
|
/* Check RX return ring producer/consumer */
|
|
|
|
ti_rxeof(sc);
|
|
|
|
|
|
|
|
/* Check TX ring producer/consumer */
|
2001-06-30 18:56:59 +04:00
|
|
|
(*sc->sc_tx_eof)(sc);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ti_handle_events(sc);
|
|
|
|
|
|
|
|
/* Re-enable interrupts. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0);
|
|
|
|
|
2000-12-14 09:42:57 +03:00
|
|
|
if ((ifp->if_flags & IFF_RUNNING) != 0 &&
|
|
|
|
IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_start(ifp);
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_stats_update(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
2001-06-30 20:34:59 +04:00
|
|
|
TI_CDSTATSSYNC(sc, BUS_DMASYNC_POSTREAD);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
ifp->if_collisions +=
|
|
|
|
(sc->ti_rdata->ti_info.ti_stats.dot3StatsSingleCollisionFrames +
|
|
|
|
sc->ti_rdata->ti_info.ti_stats.dot3StatsMultipleCollisionFrames +
|
|
|
|
sc->ti_rdata->ti_info.ti_stats.dot3StatsExcessiveCollisions +
|
|
|
|
sc->ti_rdata->ti_info.ti_stats.dot3StatsLateCollisions) -
|
|
|
|
ifp->if_collisions;
|
|
|
|
|
2001-06-30 20:34:59 +04:00
|
|
|
TI_CDSTATSSYNC(sc, BUS_DMASYNC_PREREAD);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encapsulate an mbuf chain in the tx ring by coupling the mbuf data
|
|
|
|
* pointers to descriptors.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_encap_tigon1(struct ti_softc *sc, struct mbuf *m_head, u_int32_t *txidx)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_tx_desc *f = NULL;
|
|
|
|
u_int32_t frag, cur, cnt = 0;
|
|
|
|
struct txdmamap_pool_entry *dma;
|
|
|
|
bus_dmamap_t dmamap;
|
|
|
|
int error, i;
|
2003-01-17 11:11:49 +03:00
|
|
|
struct m_tag *mtag;
|
2001-06-02 20:17:06 +04:00
|
|
|
u_int16_t csum_flags = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
dma = SIMPLEQ_FIRST(&sc->txdma_list);
|
2000-03-17 12:20:16 +03:00
|
|
|
if (dma == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
dmamap = dma->dmamap;
|
|
|
|
|
2001-07-19 20:36:14 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m_head,
|
2002-07-17 00:20:01 +04:00
|
|
|
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
|
1999-09-01 15:47:46 +04:00
|
|
|
if (error) {
|
|
|
|
struct mbuf *m;
|
2005-05-30 08:35:22 +04:00
|
|
|
int j = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
for (m = m_head; m; m = m->m_next)
|
2005-05-30 08:35:22 +04:00
|
|
|
j++;
|
1999-09-01 15:47:46 +04:00
|
|
|
printf("ti_encap: bus_dmamap_load_mbuf (len %d, %d frags) "
|
2005-05-30 08:35:22 +04:00
|
|
|
"error %d\n", m_head->m_pkthdr.len, j, error);
|
1999-09-01 15:47:46 +04:00
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
cur = frag = *txidx;
|
|
|
|
|
2001-06-02 20:17:06 +04:00
|
|
|
if (m_head->m_pkthdr.csum_flags & M_CSUM_IPv4) {
|
|
|
|
/* IP header checksum field must be 0! */
|
|
|
|
csum_flags |= TI_BDFLAG_IP_CKSUM;
|
|
|
|
}
|
|
|
|
if (m_head->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4))
|
|
|
|
csum_flags |= TI_BDFLAG_TCP_UDP_CKSUM;
|
|
|
|
|
|
|
|
/* XXX fragmented packet checksum capability? */
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < dmamap->dm_nsegs; i++) {
|
2001-06-30 18:47:23 +04:00
|
|
|
if (frag > 383)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 6144);
|
|
|
|
else if (frag > 255)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 4096);
|
|
|
|
else if (frag > 127)
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE + 2048);
|
|
|
|
else
|
|
|
|
CSR_WRITE_4(sc, TI_WINBASE,
|
|
|
|
TI_TX_RING_BASE);
|
|
|
|
f = &sc->ti_tx_ring_nic[frag % 128];
|
|
|
|
if (sc->ti_cdata.ti_tx_chain[frag] != NULL)
|
|
|
|
break;
|
|
|
|
TI_HOSTADDR(f->ti_addr) = dmamap->dm_segs[i].ds_addr;
|
|
|
|
f->ti_len = dmamap->dm_segs[i].ds_len;
|
|
|
|
f->ti_flags = csum_flags;
|
2005-02-20 18:48:35 +03:00
|
|
|
if ((mtag = VLAN_OUTPUT_TAG(&sc->ethercom, m_head))) {
|
2001-06-30 18:47:23 +04:00
|
|
|
f->ti_flags |= TI_BDFLAG_VLAN_TAG;
|
2005-02-20 18:48:35 +03:00
|
|
|
f->ti_vlan_tag = VLAN_TAG_VALUE(mtag);
|
2001-06-30 18:47:23 +04:00
|
|
|
} else {
|
|
|
|
f->ti_vlan_tag = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Sanity check: avoid coming within 16 descriptors
|
|
|
|
* of the end of the ring.
|
|
|
|
*/
|
|
|
|
if ((TI_TX_RING_CNT - (sc->ti_txcnt + cnt)) < 16)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
2001-06-30 18:47:23 +04:00
|
|
|
cur = frag;
|
|
|
|
TI_INC(frag, TI_TX_RING_CNT);
|
|
|
|
cnt++;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i < dmamap->dm_nsegs)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
if (frag == sc->ti_tx_saved_considx)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2001-06-30 18:47:23 +04:00
|
|
|
sc->ti_tx_ring_nic[cur % 128].ti_flags |=
|
|
|
|
TI_BDFLAG_END;
|
|
|
|
|
|
|
|
/* Sync the packet's DMA map. */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
sc->ti_cdata.ti_tx_chain[cur] = m_head;
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->txdma_list, link);
|
2001-06-30 18:47:23 +04:00
|
|
|
sc->txdma[cur] = dma;
|
|
|
|
sc->ti_txcnt += cnt;
|
|
|
|
|
|
|
|
*txidx = frag;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
2001-06-30 18:47:23 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_encap_tigon2(struct ti_softc *sc, struct mbuf *m_head, u_int32_t *txidx)
|
2001-06-30 18:47:23 +04:00
|
|
|
{
|
|
|
|
struct ti_tx_desc *f = NULL;
|
2001-06-30 21:02:54 +04:00
|
|
|
u_int32_t frag, firstfrag, cur, cnt = 0;
|
2001-06-30 18:47:23 +04:00
|
|
|
struct txdmamap_pool_entry *dma;
|
|
|
|
bus_dmamap_t dmamap;
|
|
|
|
int error, i;
|
2003-01-17 11:11:49 +03:00
|
|
|
struct m_tag *mtag;
|
2001-06-30 18:47:23 +04:00
|
|
|
u_int16_t csum_flags = 0;
|
|
|
|
|
|
|
|
dma = SIMPLEQ_FIRST(&sc->txdma_list);
|
|
|
|
if (dma == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
dmamap = dma->dmamap;
|
|
|
|
|
2001-07-19 20:36:14 +04:00
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m_head,
|
2002-07-17 00:20:01 +04:00
|
|
|
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
|
2001-06-30 18:47:23 +04:00
|
|
|
if (error) {
|
|
|
|
struct mbuf *m;
|
2005-05-30 08:35:22 +04:00
|
|
|
int j = 0;
|
2001-06-30 18:47:23 +04:00
|
|
|
for (m = m_head; m; m = m->m_next)
|
2005-05-30 08:35:22 +04:00
|
|
|
j++;
|
2001-06-30 18:47:23 +04:00
|
|
|
printf("ti_encap: bus_dmamap_load_mbuf (len %d, %d frags) "
|
2005-05-30 08:35:22 +04:00
|
|
|
"error %d\n", m_head->m_pkthdr.len, j, error);
|
2001-06-30 18:47:23 +04:00
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
|
2001-06-30 21:02:54 +04:00
|
|
|
cur = firstfrag = frag = *txidx;
|
2001-06-30 18:47:23 +04:00
|
|
|
|
|
|
|
if (m_head->m_pkthdr.csum_flags & M_CSUM_IPv4) {
|
|
|
|
/* IP header checksum field must be 0! */
|
|
|
|
csum_flags |= TI_BDFLAG_IP_CKSUM;
|
|
|
|
}
|
|
|
|
if (m_head->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4))
|
|
|
|
csum_flags |= TI_BDFLAG_TCP_UDP_CKSUM;
|
|
|
|
|
|
|
|
/* XXX fragmented packet checksum capability? */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < dmamap->dm_nsegs; i++) {
|
|
|
|
f = &sc->ti_rdata->ti_tx_ring[frag];
|
|
|
|
if (sc->ti_cdata.ti_tx_chain[frag] != NULL)
|
|
|
|
break;
|
|
|
|
TI_HOSTADDR(f->ti_addr) = dmamap->dm_segs[i].ds_addr;
|
|
|
|
f->ti_len = dmamap->dm_segs[i].ds_len;
|
|
|
|
f->ti_flags = csum_flags;
|
2005-02-20 18:48:35 +03:00
|
|
|
if ((mtag = VLAN_OUTPUT_TAG(&sc->ethercom, m_head))) {
|
2001-06-30 18:47:23 +04:00
|
|
|
f->ti_flags |= TI_BDFLAG_VLAN_TAG;
|
2005-02-20 18:48:35 +03:00
|
|
|
f->ti_vlan_tag = VLAN_TAG_VALUE(mtag);
|
2001-06-30 18:47:23 +04:00
|
|
|
} else {
|
|
|
|
f->ti_vlan_tag = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Sanity check: avoid coming within 16 descriptors
|
|
|
|
* of the end of the ring.
|
|
|
|
*/
|
|
|
|
if ((TI_TX_RING_CNT - (sc->ti_txcnt + cnt)) < 16)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
2001-06-30 18:47:23 +04:00
|
|
|
cur = frag;
|
|
|
|
TI_INC(frag, TI_TX_RING_CNT);
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < dmamap->dm_nsegs)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
2001-06-30 18:47:23 +04:00
|
|
|
|
|
|
|
if (frag == sc->ti_tx_saved_considx)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (ENOBUFS);
|
2001-06-30 18:47:23 +04:00
|
|
|
|
|
|
|
sc->ti_rdata->ti_tx_ring[cur].ti_flags |= TI_BDFLAG_END;
|
2001-06-30 09:48:24 +04:00
|
|
|
|
|
|
|
/* Sync the packet's DMA map. */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
2001-06-30 21:02:54 +04:00
|
|
|
/* Sync the descriptors we are using. */
|
|
|
|
TI_CDTXSYNC(sc, firstfrag, cnt, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
sc->ti_cdata.ti_tx_chain[cur] = m_head;
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_REMOVE_HEAD(&sc->txdma_list, link);
|
1999-09-01 15:47:46 +04:00
|
|
|
sc->txdma[cur] = dma;
|
|
|
|
sc->ti_txcnt += cnt;
|
|
|
|
|
|
|
|
*txidx = frag;
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Main transmit routine. To avoid having to do mbuf copies, we put pointers
|
|
|
|
* to the mbuf data regions directly in the transmit descriptors.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_start(struct ifnet *ifp)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
|
|
|
struct mbuf *m_head = NULL;
|
|
|
|
u_int32_t prodidx = 0;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
|
|
|
|
prodidx = CSR_READ_4(sc, TI_MB_SENDPROD_IDX);
|
|
|
|
|
2000-12-14 09:42:57 +03:00
|
|
|
while (sc->ti_cdata.ti_tx_chain[prodidx] == NULL) {
|
|
|
|
IFQ_POLL(&ifp->if_snd, m_head);
|
1999-09-01 15:47:46 +04:00
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pack the data into the transmit ring. If we
|
|
|
|
* don't have room, set the OACTIVE flag and wait
|
|
|
|
* for the NIC to drain the ring.
|
|
|
|
*/
|
2001-06-30 18:47:23 +04:00
|
|
|
if ((*sc->sc_tx_encap)(sc, m_head, &prodidx)) {
|
1999-09-01 15:47:46 +04:00
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2000-12-14 09:42:57 +03:00
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m_head);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
/*
|
|
|
|
* If there's a BPF listener, bounce a copy of this frame
|
|
|
|
* to him.
|
|
|
|
*/
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m_head);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Transmit */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_SENDPROD_IDX, prodidx);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
|
|
|
ifp->if_timer = 5;
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_init(void *xsc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc = xsc;
|
|
|
|
int s;
|
|
|
|
|
2001-01-14 20:37:41 +03:00
|
|
|
s = splnet();
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
/* Cancel pending I/O and flush buffers. */
|
|
|
|
ti_stop(sc);
|
|
|
|
|
|
|
|
/* Init the gen info block, ring control blocks and firmware. */
|
|
|
|
if (ti_gibinit(sc)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "initialization failure\n");
|
1999-09-01 15:47:46 +04:00
|
|
|
splx(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_init2(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
struct ifnet *ifp;
|
2007-08-30 02:33:42 +04:00
|
|
|
const u_int8_t *m;
|
1999-09-01 15:47:46 +04:00
|
|
|
struct ifmedia *ifm;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/* Specify MTU and interface index. */
|
2006-03-28 21:38:24 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_IFINDEX, device_unit(&sc->sc_dev)); /* ??? */
|
2001-06-03 07:46:43 +04:00
|
|
|
|
|
|
|
tmp = ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
|
|
|
|
if (sc->ethercom.ec_capenable & ETHERCAP_VLAN_MTU)
|
|
|
|
tmp += ETHER_VLAN_ENCAP_LEN;
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_IFMTU, tmp);
|
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_DO_CMD(TI_CMD_UPDATE_GENCOM, 0, 0);
|
|
|
|
|
|
|
|
/* Load our MAC address. */
|
2007-08-30 02:33:42 +04:00
|
|
|
m = (const u_int8_t *)CLLADDR(ifp->if_sadl);
|
1999-09-01 15:47:46 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_PAR0, (m[0] << 8) | m[1]);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_PAR1, (m[2] << 24) | (m[3] << 16)
|
|
|
|
| (m[4] << 8) | m[5]);
|
|
|
|
TI_DO_CMD(TI_CMD_SET_MAC_ADDR, 0, 0);
|
|
|
|
|
|
|
|
/* Enable or disable promiscuous mode as needed. */
|
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
|
|
TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, TI_CMD_CODE_PROMISC_ENB, 0);
|
|
|
|
} else {
|
|
|
|
TI_DO_CMD(TI_CMD_SET_PROMISC_MODE, TI_CMD_CODE_PROMISC_DIS, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Program multicast filter. */
|
|
|
|
ti_setmulti(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a Tigon 1, we should tell the
|
|
|
|
* firmware to use software packet filtering.
|
|
|
|
*/
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON) {
|
|
|
|
TI_DO_CMD(TI_CMD_FDR_FILTERING, TI_CMD_CODE_FILT_ENB, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init RX ring. */
|
|
|
|
ti_init_rx_ring_std(sc);
|
|
|
|
|
|
|
|
/* Init jumbo RX ring. */
|
2000-11-12 21:32:43 +03:00
|
|
|
if (ifp->if_mtu > (MCLBYTES - ETHER_HDR_LEN - ETHER_CRC_LEN))
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_init_rx_ring_jumbo(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a Tigon 2, we can also configure the
|
|
|
|
* mini ring.
|
|
|
|
*/
|
|
|
|
if (sc->ti_hwrev == TI_HWREV_TIGON_II)
|
|
|
|
ti_init_rx_ring_mini(sc);
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_RXRETURNCONS_IDX, 0);
|
|
|
|
sc->ti_rx_saved_considx = 0;
|
|
|
|
|
|
|
|
/* Init TX ring. */
|
|
|
|
ti_init_tx_ring(sc);
|
|
|
|
|
|
|
|
/* Tell firmware we're alive. */
|
|
|
|
TI_DO_CMD(TI_CMD_HOST_STATE, TI_CMD_CODE_STACK_UP, 0);
|
|
|
|
|
|
|
|
/* Enable host interrupts. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 0);
|
|
|
|
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure to set media properly. We have to do this
|
|
|
|
* here since we have to issue commands in order to set
|
|
|
|
* the link negotiation and we can't issue commands until
|
|
|
|
* the firmware is running.
|
|
|
|
*/
|
|
|
|
ifm = &sc->ifmedia;
|
|
|
|
tmp = ifm->ifm_media;
|
|
|
|
ifm->ifm_media = ifm->ifm_cur->ifm_media;
|
|
|
|
ti_ifmedia_upd(ifp);
|
|
|
|
ifm->ifm_media = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set media options.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_ifmedia_upd(struct ifnet *ifp)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
|
|
|
struct ifmedia *ifm;
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
ifm = &sc->ifmedia;
|
|
|
|
|
|
|
|
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
|
2007-09-08 03:05:27 +04:00
|
|
|
return (EINVAL);
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (IFM_SUBTYPE(ifm->ifm_media)) {
|
1999-09-01 15:47:46 +04:00
|
|
|
case IFM_AUTO:
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_GLINK, TI_GLNK_PREF|TI_GLNK_1000MB|
|
|
|
|
TI_GLNK_FULL_DUPLEX|TI_GLNK_RX_FLOWCTL_Y|
|
|
|
|
TI_GLNK_AUTONEGENB|TI_GLNK_ENB);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_100MB|TI_LNK_10MB|
|
|
|
|
TI_LNK_FULL_DUPLEX|TI_LNK_HALF_DUPLEX|
|
|
|
|
TI_LNK_AUTONEGENB|TI_LNK_ENB);
|
|
|
|
TI_DO_CMD(TI_CMD_LINK_NEGOTIATION,
|
|
|
|
TI_CMD_CODE_NEGOTIATE_BOTH, 0);
|
|
|
|
break;
|
2000-01-25 23:19:14 +03:00
|
|
|
case IFM_1000_SX:
|
2001-06-30 21:53:58 +04:00
|
|
|
case IFM_1000_T:
|
2000-11-17 22:33:25 +03:00
|
|
|
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_GLINK,
|
|
|
|
TI_GLNK_PREF|TI_GLNK_1000MB|TI_GLNK_FULL_DUPLEX|
|
|
|
|
TI_GLNK_RX_FLOWCTL_Y|TI_GLNK_ENB);
|
|
|
|
} else {
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_GLINK,
|
|
|
|
TI_GLNK_PREF|TI_GLNK_1000MB|
|
|
|
|
TI_GLNK_RX_FLOWCTL_Y|TI_GLNK_ENB);
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_LINK, 0);
|
|
|
|
TI_DO_CMD(TI_CMD_LINK_NEGOTIATION,
|
|
|
|
TI_CMD_CODE_NEGOTIATE_GIGABIT, 0);
|
|
|
|
break;
|
|
|
|
case IFM_100_FX:
|
|
|
|
case IFM_10_FL:
|
2000-11-17 22:33:25 +03:00
|
|
|
case IFM_100_TX:
|
|
|
|
case IFM_10_T:
|
1999-09-01 15:47:46 +04:00
|
|
|
CSR_WRITE_4(sc, TI_GCR_GLINK, 0);
|
|
|
|
CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_ENB|TI_LNK_PREF);
|
2000-11-17 22:33:25 +03:00
|
|
|
if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_FX ||
|
|
|
|
IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
|
1999-09-01 15:47:46 +04:00
|
|
|
TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_100MB);
|
|
|
|
} else {
|
|
|
|
TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_10MB);
|
|
|
|
}
|
|
|
|
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
|
|
|
|
TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_FULL_DUPLEX);
|
|
|
|
} else {
|
|
|
|
TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_HALF_DUPLEX);
|
|
|
|
}
|
|
|
|
TI_DO_CMD(TI_CMD_LINK_NEGOTIATION,
|
|
|
|
TI_CMD_CODE_NEGOTIATE_10_100, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2000-03-07 00:02:37 +03:00
|
|
|
sc->ethercom.ec_if.if_baudrate =
|
|
|
|
ifmedia_baudrate(ifm->ifm_media);
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (0);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report current media status.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
2000-11-17 22:33:25 +03:00
|
|
|
u_int32_t media = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
|
|
|
|
ifmr->ifm_status = IFM_AVALID;
|
|
|
|
ifmr->ifm_active = IFM_ETHER;
|
|
|
|
|
|
|
|
if (sc->ti_linkstat == TI_EV_CODE_LINK_DOWN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ifmr->ifm_status |= IFM_ACTIVE;
|
|
|
|
|
2000-11-17 22:33:25 +03:00
|
|
|
if (sc->ti_linkstat == TI_EV_CODE_GIG_LINK_UP) {
|
|
|
|
media = CSR_READ_4(sc, TI_GCR_GLINK_STAT);
|
|
|
|
if (sc->ti_copper)
|
2001-06-30 21:53:58 +04:00
|
|
|
ifmr->ifm_active |= IFM_1000_T;
|
2000-11-17 22:33:25 +03:00
|
|
|
else
|
|
|
|
ifmr->ifm_active |= IFM_1000_SX;
|
|
|
|
if (media & TI_GLNK_FULL_DUPLEX)
|
|
|
|
ifmr->ifm_active |= IFM_FDX;
|
|
|
|
else
|
|
|
|
ifmr->ifm_active |= IFM_HDX;
|
|
|
|
} else if (sc->ti_linkstat == TI_EV_CODE_LINK_UP) {
|
1999-09-01 15:47:46 +04:00
|
|
|
media = CSR_READ_4(sc, TI_GCR_LINK_STAT);
|
2000-11-17 22:33:25 +03:00
|
|
|
if (sc->ti_copper) {
|
|
|
|
if (media & TI_LNK_100MB)
|
|
|
|
ifmr->ifm_active |= IFM_100_TX;
|
|
|
|
if (media & TI_LNK_10MB)
|
|
|
|
ifmr->ifm_active |= IFM_10_T;
|
|
|
|
} else {
|
|
|
|
if (media & TI_LNK_100MB)
|
|
|
|
ifmr->ifm_active |= IFM_100_FX;
|
|
|
|
if (media & TI_LNK_10MB)
|
|
|
|
ifmr->ifm_active |= IFM_10_FL;
|
|
|
|
}
|
1999-09-01 15:47:46 +04:00
|
|
|
if (media & TI_LNK_FULL_DUPLEX)
|
|
|
|
ifmr->ifm_active |= IFM_FDX;
|
|
|
|
if (media & TI_LNK_HALF_DUPLEX)
|
|
|
|
ifmr->ifm_active |= IFM_HDX;
|
|
|
|
}
|
2000-03-07 00:02:37 +03:00
|
|
|
|
|
|
|
sc->ethercom.ec_if.if_baudrate =
|
|
|
|
ifmedia_baudrate(sc->ifmedia.ifm_media);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-09-08 03:05:27 +04:00
|
|
|
ti_ether_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ifaddr *ifa = (struct ifaddr *) data;
|
|
|
|
struct ti_softc *sc = ifp->if_softc;
|
|
|
|
|
2001-06-27 20:47:33 +04:00
|
|
|
if ((ifp->if_flags & IFF_UP) == 0) {
|
|
|
|
ifp->if_flags |= IFF_UP;
|
|
|
|
ti_init(sc);
|
|
|
|
}
|
2005-02-27 03:26:58 +03:00
|
|
|
|
1999-09-01 15:47:46 +04:00
|
|
|
switch (cmd) {
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
case SIOCINITIFADDR:
|
1999-09-01 15:47:46 +04:00
|
|
|
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
arp_ifinit(ifp, ifa);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static int
|
|
|
|
ti_ioctl(struct ifnet *ifp, u_long command, void *data)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc = ifp->if_softc;
|
|
|
|
struct ifreq *ifr = (struct ifreq *) data;
|
|
|
|
int s, error = 0;
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
2001-01-14 20:37:41 +03:00
|
|
|
s = splnet();
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
switch (command) {
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
case SIOCINITIFADDR:
|
1999-09-01 15:47:46 +04:00
|
|
|
error = ti_ether_ioctl(ifp, command, data);
|
|
|
|
break;
|
|
|
|
case SIOCSIFMTU:
|
2008-02-07 04:21:52 +03:00
|
|
|
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU_JUMBO)
|
1999-09-01 15:47:46 +04:00
|
|
|
error = EINVAL;
|
2008-02-07 04:21:52 +03:00
|
|
|
else if ((error = ifioctl_common(ifp, command, data)) == ENETRESET){
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_init(sc);
|
2008-02-07 04:21:52 +03:00
|
|
|
error = 0;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
if ((error = ifioctl_common(ifp, command, data)) != 0)
|
|
|
|
break;
|
1999-09-01 15:47:46 +04:00
|
|
|
if (ifp->if_flags & IFF_UP) {
|
|
|
|
/*
|
|
|
|
* If only the state of the PROMISC flag changed,
|
|
|
|
* then just use the 'set promisc mode' command
|
|
|
|
* instead of reinitializing the entire NIC. Doing
|
|
|
|
* a full re-init means reloading the firmware and
|
|
|
|
* waiting for it to start up, which may take a
|
|
|
|
* second or two.
|
|
|
|
*/
|
|
|
|
if (ifp->if_flags & IFF_RUNNING &&
|
|
|
|
ifp->if_flags & IFF_PROMISC &&
|
|
|
|
!(sc->ti_if_flags & IFF_PROMISC)) {
|
|
|
|
TI_DO_CMD(TI_CMD_SET_PROMISC_MODE,
|
|
|
|
TI_CMD_CODE_PROMISC_ENB, 0);
|
|
|
|
} else if (ifp->if_flags & IFF_RUNNING &&
|
|
|
|
!(ifp->if_flags & IFF_PROMISC) &&
|
|
|
|
sc->ti_if_flags & IFF_PROMISC) {
|
|
|
|
TI_DO_CMD(TI_CMD_SET_PROMISC_MODE,
|
|
|
|
TI_CMD_CODE_PROMISC_DIS, 0);
|
|
|
|
} else
|
|
|
|
ti_init(sc);
|
|
|
|
} else {
|
|
|
|
if (ifp->if_flags & IFF_RUNNING) {
|
|
|
|
ti_stop(sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->ti_if_flags = ifp->if_flags;
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SIOCSIFMEDIA:
|
|
|
|
case SIOCGIFMEDIA:
|
|
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
|
|
|
|
break;
|
|
|
|
default:
|
2008-02-07 04:21:52 +03:00
|
|
|
if ((error = ether_ioctl(ifp, command, data)) != ENETRESET)
|
|
|
|
break;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (command == SIOCSIFCAP)
|
|
|
|
ti_init(sc);
|
|
|
|
else if (command != SIOCADDMULTI && command != SIOCDELMULTI)
|
|
|
|
;
|
|
|
|
else if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
ti_setmulti(sc);
|
1999-09-01 15:47:46 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)splx(s);
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
return (error);
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|
|
|
|
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_watchdog(struct ifnet *ifp)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ti_softc *sc;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
|
2008-04-10 23:13:36 +04:00
|
|
|
aprint_error_dev(&sc->sc_dev, "watchdog timeout -- resetting\n");
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_stop(sc);
|
|
|
|
ti_init(sc);
|
|
|
|
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop the adapter and free any mbufs allocated to the
|
|
|
|
* RX and TX lists.
|
|
|
|
*/
|
2007-09-08 03:05:27 +04:00
|
|
|
static void
|
|
|
|
ti_stop(struct ti_softc *sc)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct ti_cmd_desc cmd;
|
|
|
|
|
|
|
|
ifp = &sc->ethercom.ec_if;
|
|
|
|
|
|
|
|
/* Disable host interrupts. */
|
|
|
|
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1);
|
|
|
|
/*
|
|
|
|
* Tell firmware we're shutting down.
|
|
|
|
*/
|
|
|
|
TI_DO_CMD(TI_CMD_HOST_STATE, TI_CMD_CODE_STACK_DOWN, 0);
|
|
|
|
|
|
|
|
/* Halt and reinitialize. */
|
|
|
|
ti_chipinit(sc);
|
|
|
|
ti_mem(sc, 0x2000, 0x100000 - 0x2000, NULL);
|
|
|
|
ti_chipinit(sc);
|
|
|
|
|
|
|
|
/* Free the RX lists. */
|
|
|
|
ti_free_rx_ring_std(sc);
|
|
|
|
|
|
|
|
/* Free jumbo RX list. */
|
|
|
|
ti_free_rx_ring_jumbo(sc);
|
|
|
|
|
|
|
|
/* Free mini RX list. */
|
|
|
|
ti_free_rx_ring_mini(sc);
|
|
|
|
|
|
|
|
/* Free TX buffers. */
|
|
|
|
ti_free_tx_ring(sc);
|
|
|
|
|
|
|
|
sc->ti_ev_prodidx.ti_idx = 0;
|
|
|
|
sc->ti_return_prodidx.ti_idx = 0;
|
|
|
|
sc->ti_tx_considx.ti_idx = 0;
|
|
|
|
sc->ti_tx_saved_considx = TI_TXCONS_UNSET;
|
|
|
|
|
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop all chip I/O so that the kernel's probe routines don't
|
|
|
|
* get confused by errant DMAs when rebooting.
|
|
|
|
*/
|
2009-09-27 16:52:59 +04:00
|
|
|
static bool
|
|
|
|
ti_shutdown(device_t self, int howto)
|
1999-09-01 15:47:46 +04:00
|
|
|
{
|
2009-09-27 16:52:59 +04:00
|
|
|
struct ti_softc *sc;
|
1999-09-01 15:47:46 +04:00
|
|
|
|
2009-09-27 16:52:59 +04:00
|
|
|
sc = device_private(self);
|
1999-09-01 15:47:46 +04:00
|
|
|
ti_chipinit(sc);
|
2009-09-27 16:52:59 +04:00
|
|
|
|
|
|
|
return true;
|
1999-09-01 15:47:46 +04:00
|
|
|
}
|