summaryrefslogtreecommitdiffstats
path: root/sys/pci/if_ti.c
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2002-06-26 03:37:47 +0000
committerken <ken@FreeBSD.org>2002-06-26 03:37:47 +0000
commit0d3a835f3f94caa05448371062a46d2485185ee1 (patch)
tree7892f78801d63bbf532093be61b62d33e321b8b8 /sys/pci/if_ti.c
parent371cb8c6de82e6b4b072b7d9b306a9c419c41175 (diff)
downloadFreeBSD-src-0d3a835f3f94caa05448371062a46d2485185ee1.zip
FreeBSD-src-0d3a835f3f94caa05448371062a46d2485185ee1.tar.gz
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes. ti.4: Update the ti(4) man page to include information on the TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options, and also include information about the new character device interface and the associated ioctls. man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated links. jumbo.9: New man page describing the jumbo buffer allocator interface and operation. zero_copy.9: New man page describing the general characteristics of the zero copy send and receive code, and what an application author should do to take advantage of the zero copy functionality. NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS, TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT. conf/files: Add uipc_jumbo.c and uipc_cow.c. conf/options: Add the 5 options mentioned above. kern_subr.c: Receive side zero copy implementation. This takes "disposable" pages attached to an mbuf, gives them to a user process, and then recycles the user's page. This is only active when ZERO_COPY_SOCKETS is turned on and the kern.ipc.zero_copy.receive sysctl variable is set to 1. uipc_cow.c: Send side zero copy functions. Takes a page written by the user and maps it copy on write and assigns it kernel virtual address space. Removes copy on write mapping once the buffer has been freed by the network stack. uipc_jumbo.c: Jumbo disposable page allocator code. This allocates (optionally) disposable pages for network drivers that want to give the user the option of doing zero copy receive. uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are enabled if ZERO_COPY_SOCKETS is turned on. Add zero copy send support to sosend() -- pages get mapped into the kernel instead of getting copied if they meet size and alignment restrictions. uipc_syscalls.c:Un-staticize some of the sf* functions so that they can be used elsewhere. (uipc_cow.c) if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid calling malloc() with M_WAITOK. Return an error if the M_NOWAIT malloc fails. The ti(4) driver and the wi(4) driver, at least, call this with a mutex held. This causes witness warnings for 'ifconfig -a' with a wi(4) or ti(4) board in the system. (I've only verified for ti(4)). ip_output.c: Fragment large datagrams so that each segment contains a multiple of PAGE_SIZE amount of data plus headers. This allows the receiver to potentially do page flipping on receives. if_ti.c: Add zero copy receive support to the ti(4) driver. If TI_PRIVATE_JUMBOS is not defined, it now uses the jumbo(9) buffer allocator for jumbo receive buffers. Add a new character device interface for the ti(4) driver for the new debugging interface. This allows (a patched version of) gdb to talk to the Tigon board and debug the firmware. There are also a few additional debugging ioctls available through this interface. Add header splitting support to the ti(4) driver. Tweak some of the default interrupt coalescing parameters to more useful defaults. Add hooks for supporting transmit flow control, but leave it turned off with a comment describing why it is turned off. if_tireg.h: Change the firmware rev to 12.4.11, since we're really at 12.4.11 plus fixes from 12.4.13. Add defines needed for debugging. Remove the ti_stats structure, it is now defined in sys/tiio.h. ti_fw.h: 12.4.11 firmware. ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13, and my header splitting patches. Revision 12.4.13 doesn't handle 10/100 negotiation properly. (This firmware is the same as what was in the tree previously, with the addition of header splitting support.) sys/jumbo.h: Jumbo buffer allocator interface. sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to indicate that the payload buffer can be thrown away / flipped to a userland process. socketvar.h: Add prototype for socow_setup. tiio.h: ioctl interface to the character portion of the ti(4) driver, plus associated structure/type definitions. uio.h: Change prototype for uiomoveco() so that we'll know whether the source page is disposable. ufs_readwrite.c:Update for new prototype of uiomoveco(). vm_fault.c: In vm_fault(), check to see whether we need to do a page based copy on write fault. vm_object.c: Add a new function, vm_object_allocate_wait(). This does the same thing that vm_object allocate does, except that it gives the caller the opportunity to specify whether it should wait on the uma_zalloc() of the object structre. This allows vm objects to be allocated while holding a mutex. (Without generating WITNESS warnings.) vm_object_allocate() is implemented as a call to vm_object_allocate_wait() with the malloc flag set to M_WAITOK. vm_object.h: Add prototype for vm_object_allocate_wait(). vm_page.c: Add page-based copy on write setup, clear and fault routines. vm_page.h: Add page based COW function prototypes and variable in the vm_page structure. Many thanks to Drew Gallatin, who wrote the zero copy send and receive code, and to all the other folks who have tested and reviewed this code over the years.
Diffstat (limited to 'sys/pci/if_ti.c')
-rw-r--r--sys/pci/if_ti.c1168
1 files changed, 1153 insertions, 15 deletions
diff --git a/sys/pci/if_ti.c b/sys/pci/if_ti.c
index dac0fb3..bc4b96e 100644
--- a/sys/pci/if_ti.c
+++ b/sys/pci/if_ti.c
@@ -78,6 +78,8 @@
* - Andrew Gallatin for providing FreeBSD/Alpha support.
*/
+#include "opt_ti.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
@@ -86,6 +88,7 @@
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/queue.h>
+#include <sys/conf.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -109,20 +112,57 @@
#include <sys/bus.h>
#include <sys/rman.h>
+/* #define TI_PRIVATE_JUMBOS */
+
+#if !defined(TI_PRIVATE_JUMBOS)
+#include <sys/sockio.h>
+#include <sys/uio.h>
+#include <sys/lock.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_map.h>
+#include <vm/vm_param.h>
+#include <vm/vm_pageout.h>
+#include <sys/vmmeter.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_kern.h>
+#include <sys/proc.h>
+#include <sys/jumbo.h>
+#endif /* !TI_PRIVATE_JUMBOS */
+#include <sys/vnode.h> /* for vfindev, vgone */
+
#include <pci/pcireg.h>
#include <pci/pcivar.h>
+#include <sys/tiio.h>
#include <pci/if_tireg.h>
#include <pci/ti_fw.h>
#include <pci/ti_fw2.h>
#define TI_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_IP_FRAGS)
+/*
+ * We can only turn on header splitting if we're using extended receive
+ * BDs.
+ */
+#if defined(TI_JUMBO_HDRSPLIT) && defined(TI_PRIVATE_JUMBOS)
+#error "options TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS are mutually exclusive"
+#endif /* TI_JUMBO_HDRSPLIT && TI_JUMBO_HDRSPLIT */
#if !defined(lint)
static const char rcsid[] =
"$FreeBSD$";
#endif
+struct ti_softc *tis[8];
+
+typedef enum {
+ TI_SWAP_HTON,
+ TI_SWAP_NTOH
+} ti_swap_type;
+
+
/*
* Various supported device vendors/types and their names.
*/
@@ -145,6 +185,28 @@ static struct ti_type ti_devs[] = {
{ 0, 0, NULL }
};
+#define TI_CDEV_MAJOR 153
+
+static d_open_t ti_open;
+static d_close_t ti_close;
+static d_ioctl_t ti_ioctl2;
+
+static struct cdevsw ti_cdevsw = {
+ /* open */ ti_open,
+ /* close */ ti_close,
+ /* read */ NULL,
+ /* write */ NULL,
+ /* ioctl */ ti_ioctl2,
+ /* poll */ seltrue,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "ti",
+ /* maj */ TI_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
static int ti_probe (device_t);
static int ti_attach (device_t);
static int ti_detach (device_t);
@@ -175,14 +237,22 @@ static void ti_setmulti (struct ti_softc *);
static void ti_mem (struct ti_softc *, u_int32_t,
u_int32_t, caddr_t);
+static int ti_copy_mem (struct ti_softc *, u_int32_t,
+ u_int32_t, caddr_t, int, int);
+static int ti_copy_scratch (struct ti_softc *, u_int32_t,
+ u_int32_t, caddr_t, int, int, int);
+static int ti_bcopy_swap (const void *, void *, size_t,
+ ti_swap_type);
static void ti_loadfw (struct ti_softc *);
static void ti_cmd (struct ti_softc *, struct ti_cmd_desc *);
static void ti_cmd_ext (struct ti_softc *, struct ti_cmd_desc *,
caddr_t, int);
static void ti_handle_events (struct ti_softc *);
+#ifdef TI_PRIVATE_JUMBOS
static int ti_alloc_jumbo_mem (struct ti_softc *);
static void *ti_jalloc (struct ti_softc *);
static void ti_jfree (caddr_t, void *);
+#endif /* TI_PRIVATE_JUMBOS */
static int ti_newbuf_std (struct ti_softc *, int, struct mbuf *);
static int ti_newbuf_mini (struct ti_softc *, int, struct mbuf *);
static int ti_newbuf_jumbo (struct ti_softc *, int, struct mbuf *);
@@ -199,6 +269,11 @@ static int ti_64bitslot_war (struct ti_softc *);
static int ti_chipinit (struct ti_softc *);
static int ti_gibinit (struct ti_softc *);
+#ifdef TI_JUMBO_HDRSPLIT
+static __inline void ti_hdr_split __P((struct mbuf *top, int hdr_len,
+ int pkt_len, int idx));
+#endif /* TI_JUMBO_HDRSPLIT */
+
static device_method_t ti_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_probe),
@@ -218,6 +293,20 @@ static devclass_t ti_devclass;
DRIVER_MODULE(if_ti, pci, ti_driver, ti_devclass, 0, 0);
+/* List of Tigon softcs */
+static STAILQ_HEAD(ti_softc_list, ti_softc) ti_sc_list;
+
+static struct ti_softc *
+ti_lookup_softc(int unit)
+{
+ struct ti_softc *sc;
+ for (sc = STAILQ_FIRST(&ti_sc_list); sc != NULL;
+ sc = STAILQ_NEXT(sc, ti_links))
+ if (sc->ti_unit == unit)
+ return(sc);
+ return(NULL);
+}
+
/*
* Send an instruction or address to the EEPROM, check for ACK.
*/
@@ -398,6 +487,338 @@ static void ti_mem(sc, addr, len, buf)
return;
}
+static int
+ti_copy_mem(sc, tigon_addr, len, buf, useraddr, readdata)
+ struct ti_softc *sc;
+ u_int32_t tigon_addr, len;
+ caddr_t buf;
+ int useraddr, readdata;
+{
+ int segptr, segsize, cnt;
+ caddr_t ptr;
+ u_int32_t origwin;
+ u_int8_t tmparray[TI_WINLEN], tmparray2[TI_WINLEN];
+ int resid, segresid;
+ int first_pass;
+
+ /*
+ * At the moment, we don't handle non-aligned cases, we just bail.
+ * If this proves to be a problem, it will be fixed.
+ */
+ if ((readdata == 0)
+ && (tigon_addr & 0x3)) {
+ printf("ti%d: ti_copy_mem: tigon address %#x isn't "
+ "word-aligned\n", sc->ti_unit, tigon_addr);
+ printf("ti%d: ti_copy_mem: unaligned writes aren't yet "
+ "supported\n", sc->ti_unit);
+ return(EINVAL);
+ }
+
+ segptr = tigon_addr & ~0x3;
+ segresid = tigon_addr - segptr;
+
+ /*
+ * This is the non-aligned amount left over that we'll need to
+ * copy.
+ */
+ resid = len & 0x3;
+
+ /* Add in the left over amount at the front of the buffer */
+ resid += segresid;
+
+ cnt = len & ~0x3;
+ /*
+ * If resid + segresid is >= 4, add multiples of 4 to the count and
+ * decrease the residual by that much.
+ */
+ cnt += resid & ~0x3;
+ resid -= resid & ~0x3;
+
+ ptr = buf;
+
+ first_pass = 1;
+
+ /*
+ * Make sure we aren't interrupted while we're changing the window
+ * pointer.
+ */
+ TI_LOCK(sc);
+
+ /*
+ * Save the old window base value.
+ */
+ origwin = CSR_READ_4(sc, TI_WINBASE);
+
+ while(cnt) {
+ bus_size_t ti_offset;
+
+ if (cnt < TI_WINLEN)
+ segsize = cnt;
+ else
+ segsize = TI_WINLEN - (segptr % TI_WINLEN);
+ CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1)));
+
+ ti_offset = TI_WINDOW + (segptr & (TI_WINLEN -1));
+
+ if (readdata) {
+
+ bus_space_read_region_4(sc->ti_btag,
+ sc->ti_bhandle, ti_offset,
+ (u_int32_t *)tmparray,
+ segsize >> 2);
+ if (useraddr) {
+ /*
+ * Yeah, this is a little on the kludgy
+ * side, but at least this code is only
+ * used for debugging.
+ */
+ ti_bcopy_swap(tmparray, tmparray2, segsize,
+ TI_SWAP_NTOH);
+
+ if (first_pass) {
+ copyout(&tmparray2[segresid], ptr,
+ segsize - segresid);
+ first_pass = 0;
+ } else
+ copyout(tmparray2, ptr, segsize);
+ } else {
+ if (first_pass) {
+
+ ti_bcopy_swap(tmparray, tmparray2,
+ segsize, TI_SWAP_NTOH);
+ bcopy(&tmparray2[segresid], ptr,
+ segsize - segresid);
+ first_pass = 0;
+ } else
+ ti_bcopy_swap(tmparray, ptr, segsize,
+ TI_SWAP_NTOH);
+ }
+
+ } else {
+ if (useraddr) {
+ copyin(ptr, tmparray2, segsize);
+ ti_bcopy_swap(tmparray2, tmparray, segsize,
+ TI_SWAP_HTON);
+ } else
+ ti_bcopy_swap(ptr, tmparray, segsize,
+ TI_SWAP_HTON);
+
+ bus_space_write_region_4(sc->ti_btag,
+ sc->ti_bhandle, ti_offset,
+ (u_int32_t *)tmparray,
+ segsize >> 2);
+ }
+ segptr += segsize;
+ ptr += segsize;
+ cnt -= segsize;
+ }
+
+ /*
+ * Handle leftover, non-word-aligned bytes.
+ */
+ if (resid != 0) {
+ u_int32_t tmpval, tmpval2;
+ bus_size_t ti_offset;
+
+ /*
+ * Set the segment pointer.
+ */
+ CSR_WRITE_4(sc, TI_WINBASE, (segptr & ~(TI_WINLEN - 1)));
+
+ ti_offset = TI_WINDOW + (segptr & (TI_WINLEN - 1));
+
+ /*
+ * First, grab whatever is in our source/destination.
+ * We'll obviously need this for reads, but also for
+ * writes, since we'll be doing read/modify/write.
+ */
+ bus_space_read_region_4(sc->ti_btag, sc->ti_bhandle,
+ ti_offset, &tmpval, 1);
+
+ /*
+ * Next, translate this from little-endian to big-endian
+ * (at least on i386 boxes).
+ */
+ tmpval2 = ntohl(tmpval);
+
+ if (readdata) {
+ /*
+ * If we're reading, just copy the leftover number
+ * of bytes from the host byte order buffer to
+ * the user's buffer.
+ */
+ if (useraddr)
+ copyout(&tmpval2, ptr, resid);
+ else
+ bcopy(&tmpval2, ptr, resid);
+ } else {
+ /*
+ * If we're writing, first copy the bytes to be
+ * written into the network byte order buffer,
+ * leaving the rest of the buffer with whatever was
+ * originally in there. Then, swap the bytes
+ * around into host order and write them out.
+ *
+ * XXX KDM the read side of this has been verified
+ * to work, but the write side of it has not been
+ * verified. So user beware.
+ */
+ if (useraddr)
+ copyin(ptr, &tmpval2, resid);
+ else
+ bcopy(ptr, &tmpval2, resid);
+
+ tmpval = htonl(tmpval2);
+
+ bus_space_write_region_4(sc->ti_btag, sc->ti_bhandle,
+ ti_offset, &tmpval, 1);
+ }
+ }
+
+ CSR_WRITE_4(sc, TI_WINBASE, origwin);
+
+ TI_UNLOCK(sc);
+
+ return(0);
+}
+
+static int
+ti_copy_scratch(sc, tigon_addr, len, buf, useraddr, readdata, cpu)
+ struct ti_softc *sc;
+ u_int32_t tigon_addr, len;
+ caddr_t buf;
+ int useraddr, readdata;
+ int cpu;
+{
+ u_int32_t segptr;
+ int cnt;
+ u_int32_t tmpval, tmpval2;
+ caddr_t ptr;
+
+ /*
+ * At the moment, we don't handle non-aligned cases, we just bail.
+ * If this proves to be a problem, it will be fixed.
+ */
+ if (tigon_addr & 0x3) {
+ printf("ti%d: ti_copy_scratch: tigon address %#x isn't "
+ "word-aligned\n", sc->ti_unit, tigon_addr);
+ return(EINVAL);
+ }
+
+ if (len & 0x3) {
+ printf("ti%d: ti_copy_scratch: transfer length %d isn't "
+ "word-aligned\n", sc->ti_unit, len);
+ return(EINVAL);
+ }
+
+ segptr = tigon_addr;
+ cnt = len;
+ ptr = buf;
+
+ TI_LOCK(sc);
+
+ while (cnt) {
+ CSR_WRITE_4(sc, CPU_REG(TI_SRAM_ADDR, cpu), segptr);
+
+ if (readdata) {
+ tmpval2 = CSR_READ_4(sc, CPU_REG(TI_SRAM_DATA, cpu));
+
+ tmpval = ntohl(tmpval2);
+
+ /*
+ * Note: I've used this debugging interface
+ * extensively with Alteon's 12.3.15 firmware,
+ * compiled with GCC 2.7.2.1 and binutils 2.9.1.
+ *
+ * When you compile the firmware without
+ * optimization, which is necessary sometimes in
+ * order to properly step through it, you sometimes
+ * read out a bogus value of 0xc0017c instead of
+ * whatever was supposed to be in that scratchpad
+ * location. That value is on the stack somewhere,
+ * but I've never been able to figure out what was
+ * causing the problem.
+ *
+ * The address seems to pop up in random places,
+ * often not in the same place on two subsequent
+ * reads.
+ *
+ * In any case, the underlying data doesn't seem
+ * to be affected, just the value read out.
+ *
+ * KDM, 3/7/2000
+ */
+
+ if (tmpval2 == 0xc0017c)
+ printf("ti%d: found 0xc0017c at %#x "
+ "(tmpval2)\n", sc->ti_unit, segptr);
+
+ if (tmpval == 0xc0017c)
+ printf("ti%d: found 0xc0017c at %#x "
+ "(tmpval)\n", sc->ti_unit, segptr);
+
+ if (useraddr)
+ copyout(&tmpval, ptr, 4);
+ else
+ bcopy(&tmpval, ptr, 4);
+ } else {
+ if (useraddr)
+ copyin(ptr, &tmpval2, 4);
+ else
+ bcopy(ptr, &tmpval2, 4);
+
+ tmpval = htonl(tmpval2);
+
+ CSR_WRITE_4(sc, CPU_REG(TI_SRAM_DATA, cpu), tmpval);
+ }
+
+ cnt -= 4;
+ segptr += 4;
+ ptr += 4;
+ }
+
+ TI_UNLOCK(sc);
+
+ return(0);
+}
+
+static int
+ti_bcopy_swap(src, dst, len, swap_type)
+ const void *src;
+ void *dst;
+ size_t len;
+ ti_swap_type swap_type;
+{
+ const u_int8_t *tmpsrc;
+ u_int8_t *tmpdst;
+ size_t tmplen;
+
+ if (len & 0x3) {
+ printf("ti_bcopy_swap: length %d isn't 32-bit aligned\n",
+ len);
+ return(-1);
+ }
+
+ tmpsrc = src;
+ tmpdst = dst;
+ tmplen = len;
+
+ while (tmplen) {
+ if (swap_type == TI_SWAP_NTOH)
+ *(u_int32_t *)tmpdst =
+ ntohl(*(const u_int32_t *)tmpsrc);
+ else
+ *(u_int32_t *)tmpdst =
+ htonl(*(const u_int32_t *)tmpsrc);
+
+ tmpsrc += 4;
+ tmpdst += 4;
+ tmplen -= 4;
+ }
+
+ return(0);
+}
+
/*
* 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
@@ -563,6 +984,8 @@ static void ti_handle_events(sc)
return;
}
+#ifdef TI_PRIVATE_JUMBOS
+
/*
* 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,
@@ -684,6 +1107,7 @@ static void ti_jfree(buf, args)
return;
}
+#endif /* TI_PRIVATE_JUMBOS */
/*
* Intialize a standard receive ring descriptor.
@@ -765,6 +1189,8 @@ static int ti_newbuf_mini(sc, i, m)
return(0);
}
+#ifdef TI_PRIVATE_JUMBOS
+
/*
* Initialize a jumbo receive ring descriptor. This allocates
* a jumbo buffer from the pool managed internally by the driver.
@@ -821,6 +1247,153 @@ static int ti_newbuf_jumbo(sc, i, m)
return(0);
}
+#else
+#include <vm/vm_page.h>
+
+#if (PAGE_SIZE == 4096)
+#define NPAYLOAD 2
+#else
+#define NPAYLOAD 1
+#endif
+
+#define TCP_HDR_LEN (52 + sizeof(struct ether_header))
+#define UDP_HDR_LEN (28 + sizeof(struct ether_header))
+#define NFS_HDR_LEN (UDP_HDR_LEN)
+int HDR_LEN = TCP_HDR_LEN;
+
+
+ /*
+ * Initialize a jumbo receive ring descriptor. This allocates
+ * a jumbo buffer from the pool managed internally by the driver.
+ */
+static int
+ti_newbuf_jumbo(sc, idx, m_old)
+ struct ti_softc *sc;
+ int idx;
+ struct mbuf *m_old;
+{
+ struct mbuf *cur, *m_new = NULL;
+ struct mbuf *m[3] = {NULL, NULL, NULL};
+ struct ti_rx_desc_ext *r;
+ vm_page_t frame;
+ /* 1 extra buf to make nobufs easy*/
+ caddr_t buf[3] = {NULL, NULL, NULL};
+ int i;
+
+ if (m_old != NULL) {
+ m_new = m_old;
+ cur = m_old->m_next;
+ for (i = 0; i <= NPAYLOAD; i++){
+ m[i] = cur;
+ cur = cur->m_next;
+ }
+ } else {
+ /* Allocate the mbufs. */
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("ti%d: mbuf allocation failed "
+ "-- packet dropped!\n", sc->ti_unit);
+ goto nobufs;
+ }
+ MGET(m[NPAYLOAD], M_DONTWAIT, MT_DATA);
+ if (m[NPAYLOAD] == NULL) {
+ printf("ti%d: cluster mbuf allocation failed "
+ "-- packet dropped!\n", sc->ti_unit);
+ goto nobufs;
+ }
+ MCLGET(m[NPAYLOAD], M_DONTWAIT);
+ if ((m[NPAYLOAD]->m_flags & M_EXT) == 0) {
+ printf("ti%d: mbuf allocation failed "
+ "-- packet dropped!\n", sc->ti_unit);
+ goto nobufs;
+ }
+ m[NPAYLOAD]->m_len = MCLBYTES;
+
+ for (i = 0; i < NPAYLOAD; i++){
+ MGET(m[i], M_DONTWAIT, MT_DATA);
+ if (m[i] == NULL) {
+ printf("ti%d: mbuf allocation failed "
+ "-- packet dropped!\n", sc->ti_unit);
+ goto nobufs;
+ }
+ if (!(frame = jumbo_pg_alloc())){
+ printf("ti%d: buffer allocation failed "
+ "-- packet dropped!\n", sc->ti_unit);
+ printf(" index %d page %d\n", idx, i);
+ goto nobufs;
+ }
+ buf[i] = jumbo_phys_to_kva(VM_PAGE_TO_PHYS(frame));
+ }
+ for (i = 0; i < NPAYLOAD; i++){
+ /* Attach the buffer to the mbuf. */
+ m[i]->m_data = (void *)buf[i];
+ m[i]->m_len = PAGE_SIZE;
+ MEXTADD(m[i], (void *)buf[i], PAGE_SIZE,
+ jumbo_freem, NULL, 0, EXT_DISPOSABLE);
+ m[i]->m_next = m[i+1];
+ }
+ /* link the buffers to the header */
+ m_new->m_next = m[0];
+ m_new->m_data += ETHER_ALIGN;
+ if (sc->ti_hdrsplit)
+ m_new->m_len = MHLEN - ETHER_ALIGN;
+ else
+ m_new->m_len = HDR_LEN;
+ m_new->m_pkthdr.len = NPAYLOAD * PAGE_SIZE + m_new->m_len;
+ }
+
+ /* Set up the descriptor. */
+ r = &sc->ti_rdata->ti_rx_jumbo_ring[idx];
+ sc->ti_cdata.ti_rx_jumbo_chain[idx] = m_new;
+ TI_HOSTADDR(r->ti_addr0) = vtophys(mtod(m_new, caddr_t));
+ r->ti_len0 = m_new->m_len;
+
+ TI_HOSTADDR(r->ti_addr1) = vtophys(mtod(m[0], caddr_t));
+ r->ti_len1 = PAGE_SIZE;
+
+ TI_HOSTADDR(r->ti_addr2) = vtophys(mtod(m[1], caddr_t));
+ r->ti_len2 = m[1]->m_ext.ext_size; /* could be PAGE_SIZE or MCLBYTES */
+
+ if (PAGE_SIZE == 4096) {
+ TI_HOSTADDR(r->ti_addr3) = vtophys(mtod(m[2], caddr_t));
+ r->ti_len3 = MCLBYTES;
+ } else {
+ r->ti_len3 = 0;
+ }
+ r->ti_type = TI_BDTYPE_RECV_JUMBO_BD;
+
+ r->ti_flags = TI_BDFLAG_JUMBO_RING|TI_RCB_FLAG_USE_EXT_RX_BD;
+
+ if (sc->arpcom.ac_if.if_hwassist)
+ r->ti_flags |= TI_BDFLAG_TCP_UDP_CKSUM|TI_BDFLAG_IP_CKSUM;
+
+ r->ti_idx = idx;
+
+ return(0);
+
+ nobufs:
+
+ /*
+ * Warning! :
+ * This can only be called before the mbufs are strung together.
+ * If the mbufs are strung together, m_freem() will free the chain,
+ * so that the later mbufs will be freed multiple times.
+ */
+ if (m_new)
+ m_freem(m_new);
+
+ for(i = 0; i < 3; i++){
+ if (m[i])
+ m_freem(m[i]);
+ if (buf[i])
+ jumbo_pg_free((vm_offset_t)buf[i]);
+ }
+ return ENOBUFS;
+}
+#endif
+
+
+
/*
* 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
@@ -1117,6 +1690,7 @@ static int ti_chipinit(sc)
{
u_int32_t cacheline;
u_int32_t pci_writemax = 0;
+ u_int32_t hdrsplit;
/* Initialize link to down state. */
sc->ti_linkstat = TI_EV_CODE_LINK_DOWN;
@@ -1164,6 +1738,18 @@ static int ti_chipinit(sc)
TI_SETBIT(sc, TI_MISC_CONF, TI_MCR_SRAM_SYNCHRONOUS);
}
+ /*
+ * We don't have firmware source for the Tigon 1, so Tigon 1 boards
+ * can't do header splitting.
+ */
+#ifdef TI_JUMBO_HDRSPLIT
+ if (sc->ti_hwrev != TI_HWREV_TIGON)
+ sc->ti_hdrsplit = 1;
+ else
+ printf("ti%d: can't do header splitting on a Tigon I board\n",
+ sc->ti_unit);
+#endif /* TI_JUMBO_HDRSPLIT */
+
/* 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) {
@@ -1220,17 +1806,22 @@ static int ti_chipinit(sc)
/* This sets the min dma param all the way up (0xff). */
TI_SETBIT(sc, TI_PCI_STATE, TI_PCISTATE_MINDMA);
+ if (sc->ti_hdrsplit)
+ hdrsplit = TI_OPMODE_JUMBO_HDRSPLIT;
+ else
+ hdrsplit = 0;
+
/* 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
+ TI_OPMODE_DONT_FRAG_JUMBO | hdrsplit);
+#else /* BYTE_ORDER */
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
+ TI_OPMODE_WARN_ENB|TI_OPMODE_FATAL_ENB | hdrsplit);
+#endif /* BYTE_ORDER */
/*
* Only allow 1 DMA channel to be active at a time.
@@ -1327,8 +1918,14 @@ static int ti_gibinit(sc)
rcb = &sc->ti_rdata->ti_info.ti_jumbo_rx_rcb;
TI_HOSTADDR(rcb->ti_hostaddr) =
vtophys(&sc->ti_rdata->ti_rx_jumbo_ring);
+
+#ifdef TI_PRIVATE_JUMBOS
rcb->ti_max_len = TI_JUMBO_FRAMELEN;
rcb->ti_flags = 0;
+#else
+ rcb->ti_max_len = PAGE_SIZE;
+ rcb->ti_flags = TI_RCB_FLAG_USE_EXT_RX_BD;
+#endif
if (sc->arpcom.ac_if.if_hwassist)
rcb->ti_flags |= TI_RCB_FLAG_TCP_UDP_CKSUM |
TI_RCB_FLAG_IP_CKSUM | TI_RCB_FLAG_NO_PHDR_CKSUM;
@@ -1398,10 +1995,12 @@ static int ti_gibinit(sc)
vtophys(&sc->ti_tx_considx);
/* Set up tuneables */
+#if 0
if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN))
CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS,
(sc->ti_rx_coal_ticks / 10));
else
+#endif
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);
@@ -1442,6 +2041,38 @@ static int ti_probe(dev)
return(ENXIO);
}
+#ifdef KLD_MODULE
+static int
+log2rndup(int len)
+{
+ int log2size = 0, t = len;
+ while (t > 1) {
+ log2size++;
+ t >>= 1;
+ }
+ if (len != (1 << log2size))
+ log2size++;
+ return log2size;
+}
+
+static int
+ti_mbuf_sanity(device_t dev)
+{
+ if ((mbstat.m_msize != MSIZE) || mbstat.m_mclbytes != MCLBYTES){
+ device_printf(dev, "\n");
+ device_printf(dev, "This module was compiled with "
+ "-DMCLSHIFT=%d -DMSIZE=%d\n", MCLSHIFT,
+ MSIZE);
+ device_printf(dev, "The kernel was compiled with MCLSHIFT=%d,"
+ " MSIZE=%d\n", log2rndup(mbstat.m_mclbytes),
+ (int)mbstat.m_msize);
+ return(EINVAL);
+ }
+ return(0);
+}
+#endif
+
+
static int ti_attach(dev)
device_t dev;
{
@@ -1450,13 +2081,26 @@ static int ti_attach(dev)
struct ti_softc *sc;
int unit, error = 0, rid;
+ sc = NULL;
+
+#ifdef KLD_MODULE
+ if (ti_mbuf_sanity(dev)){
+ device_printf(dev, "Module mbuf constants do not match "
+ "kernel constants!\n");
+ device_printf(dev, "Rebuild the module or the kernel so "
+ "they match\n");
+ device_printf(dev, "\n");
+ error = EINVAL;
+ goto fail;
+ }
+#endif
+
sc = device_get_softc(dev);
unit = device_get_unit(dev);
bzero(sc, sizeof(struct ti_softc));
mtx_init(&sc->ti_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
- TI_LOCK(sc);
sc->arpcom.ac_if.if_capabilities = IFCAP_HWCSUM;
sc->arpcom.ac_if.if_capenable = sc->arpcom.ac_if.if_capabilities;
@@ -1577,6 +2221,7 @@ static int ti_attach(dev)
bzero(sc->ti_rdata, sizeof(struct ti_ring_data));
/* Try to allocate memory for jumbo buffers. */
+#ifdef TI_PRIVATE_JUMBOS
if (ti_alloc_jumbo_mem(sc)) {
printf("ti%d: jumbo buffer allocation failed\n", sc->ti_unit);
bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand);
@@ -1588,6 +2233,18 @@ static int ti_attach(dev)
error = ENXIO;
goto fail;
}
+#else
+ if (!jumbo_vm_init()) {
+ printf("ti%d: VM initialization failed!\n", sc->ti_unit);
+ bus_teardown_intr(dev, sc->ti_irq, sc->ti_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq);
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ TI_PCI_LOMEM, sc->ti_res);
+ free(sc->ti_rdata, M_DEVBUF);
+ error = ENOMEM;
+ goto fail;
+ }
+#endif
/*
* We really need a better way to tell a 1000baseTX card
@@ -1606,10 +2263,16 @@ static int ti_attach(dev)
/* Set default tuneable values. */
sc->ti_stat_ticks = 2 * TI_TICKS_PER_SEC;
+#if 0
sc->ti_rx_coal_ticks = TI_TICKS_PER_SEC / 5000;
+#endif
+ sc->ti_rx_coal_ticks = 170;
sc->ti_tx_coal_ticks = TI_TICKS_PER_SEC / 500;
sc->ti_rx_max_coal_bds = 64;
+#if 0
sc->ti_tx_max_coal_bds = 128;
+#endif
+ sc->ti_tx_max_coal_bds = 32;
sc->ti_tx_buf_ratio = 21;
/* Set up ifnet structure */
@@ -1618,6 +2281,7 @@ static int ti_attach(dev)
ifp->if_unit = sc->ti_unit;
ifp->if_name = "ti";
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ tis[unit] = sc;
ifp->if_ioctl = ti_ioctl;
ifp->if_output = ether_output;
ifp->if_start = ti_start;
@@ -1656,24 +2320,70 @@ static int ti_attach(dev)
ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO);
/*
+ * We're assuming here that card initialization is a sequential
+ * thing. If it isn't, multiple cards probing at the same time
+ * could stomp on the list of softcs here.
+ */
+ /*
+ * If this is the first card to be initialized, initialize the
+ * softc queue.
+ */
+ if (unit == 0)
+ STAILQ_INIT(&ti_sc_list);
+
+ STAILQ_INSERT_TAIL(&ti_sc_list, sc, ti_links);
+
+ /* Register the device */
+ sc->dev = make_dev(&ti_cdevsw, sc->ti_unit, UID_ROOT, GID_OPERATOR,
+ 0600, "ti%d", sc->ti_unit);
+
+ /*
* Call MI attach routine.
*/
ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
- TI_UNLOCK(sc);
return(0);
fail:
- TI_UNLOCK(sc);
mtx_destroy(&sc->ti_mtx);
return(error);
}
+/*
+ * Verify that our character special device is not currently
+ * open. Also track down any cached vnodes & kill them before
+ * the module is unloaded
+ */
+static int
+ti_unref_special(device_t dev)
+{
+ struct vnode *ti_vn;
+ int count;
+ struct ti_softc *sc = sc = device_get_softc(dev);
+
+ if (!vfinddev(sc->dev, VCHR, &ti_vn)) {
+ return 0;
+ }
+
+ if ((count = vcount(ti_vn))) {
+ device_printf(dev, "%d refs to special device, "
+ "denying unload\n", count);
+ return count;
+ }
+ /* now we know that there's a vnode in the cache. We hunt it
+ down and kill it now, before unloading */
+ vgone(ti_vn);
+ return(0);
+}
+
+
static int ti_detach(dev)
device_t dev;
{
struct ti_softc *sc;
struct ifnet *ifp;
+ if (ti_unref_special(dev))
+ return EBUSY;
sc = device_get_softc(dev);
TI_LOCK(sc);
@@ -1686,7 +2396,9 @@ static int ti_detach(dev)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ti_irq);
bus_release_resource(dev, SYS_RES_MEMORY, TI_PCI_LOMEM, sc->ti_res);
+#ifdef TI_PRIVATE_JUMBOS
contigfree(sc->ti_cdata.ti_jumbo_buf, TI_JMEM, M_DEVBUF);
+#endif
contigfree(sc->ti_rdata, sizeof(struct ti_ring_data), M_DEVBUF);
ifmedia_removeall(&sc->ifmedia);
@@ -1696,6 +2408,63 @@ static int ti_detach(dev)
return(0);
}
+#ifdef TI_JUMBO_HDRSPLIT
+/*
+ * If hdr_len is 0, that means that header splitting wasn't done on
+ * this packet for some reason. The two most likely reasons are that
+ * the protocol isn't a supported protocol for splitting, or this
+ * packet had a fragment offset that wasn't 0.
+ *
+ * The header length, if it is non-zero, will always be the length of
+ * the headers on the packet, but that length could be longer than the
+ * first mbuf. So we take the minimum of the two as the actual
+ * length.
+ */
+static __inline void
+ti_hdr_split(struct mbuf *top, int hdr_len, int pkt_len, int idx)
+{
+ int i = 0;
+ int lengths[4] = {0, 0, 0, 0};
+ struct mbuf *m, *mp;
+
+ if (hdr_len != 0)
+ top->m_len = min(hdr_len, top->m_len);
+ pkt_len -= top->m_len;
+ lengths[i++] = top->m_len;
+
+ mp = top;
+ for (m = top->m_next; m && pkt_len; m = m->m_next) {
+ m->m_len = m->m_ext.ext_size = min(m->m_len, pkt_len);
+ pkt_len -= m->m_len;
+ lengths[i++] = m->m_len;
+ mp = m;
+ }
+
+#if 0
+ if (hdr_len != 0)
+ printf("got split packet: ");
+ else
+ printf("got non-split packet: ");
+
+ printf("%d,%d,%d,%d = %d\n", lengths[0],
+ lengths[1], lengths[2], lengths[3],
+ lengths[0] + lengths[1] + lengths[2] +
+ lengths[3]);
+#endif
+
+ if (pkt_len)
+ panic("header splitting didn't");
+
+ if (m) {
+ m_freem(m);
+ mp->m_next = NULL;
+
+ }
+ if (mp->m_next != NULL)
+ panic("ti_hdr_split: last mbuf in chain should be null");
+}
+#endif /* TI_JUMBO_HDRSPLIT */
+
/*
* Frame reception handling. This is called if there's a frame
* on the receive return list.
@@ -1734,6 +2503,7 @@ static void ti_rxeof(sc)
}
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;
@@ -1747,6 +2517,17 @@ static void ti_rxeof(sc)
ti_newbuf_jumbo(sc, sc->ti_jumbo, m);
continue;
}
+#ifdef TI_PRIVATE_JUMBOS
+ m->m_len = cur_rx->ti_len;
+#else /* TI_PRIVATE_JUMBOS */
+#ifdef TI_JUMBO_HDRSPLIT
+ if (sc->ti_hdrsplit)
+ ti_hdr_split(m, TI_HOSTADDR(cur_rx->ti_addr),
+ cur_rx->ti_len, rxidx);
+ else
+#endif /* TI_JUMBO_HDRSPLIT */
+ m_adj(m, cur_rx->ti_len - m->m_pkthdr.len);
+#endif /* TI_PRIVATE_JUMBOS */
} 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];
@@ -1761,6 +2542,7 @@ static void ti_rxeof(sc)
ti_newbuf_mini(sc, sc->ti_mini, m);
continue;
}
+ m->m_len = cur_rx->ti_len;
} else {
TI_INC(sc->ti_std, TI_STD_RX_RING_CNT);
m = sc->ti_cdata.ti_rx_std_chain[rxidx];
@@ -1775,9 +2557,10 @@ static void ti_rxeof(sc)
ti_newbuf_std(sc, sc->ti_std, m);
continue;
}
+ m->m_len = cur_rx->ti_len;
}
- m->m_pkthdr.len = m->m_len = cur_rx->ti_len;
+ m->m_pkthdr.len = cur_rx->ti_len;
ifp->if_ipackets++;
eh = mtod(m, struct ether_header *);
m->m_pkthdr.rcvif = ifp;
@@ -1876,14 +2659,14 @@ static void ti_intr(xsc)
TI_LOCK(sc);
ifp = &sc->arpcom.ac_if;
-#ifdef notdef
+/*#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)) {
TI_UNLOCK(sc);
return;
}
-#endif
+/*#endif*/
/* Ack interrupt and stop others from occuring. */
CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1);
@@ -2015,7 +2798,7 @@ static int ti_encap(sc, m_head, txidx)
if (sc->ti_hwrev == TI_HWREV_TIGON)
sc->ti_rdata->ti_tx_ring_nic[cur % 128].ti_flags |=
- TI_BDFLAG_END;
+ TI_BDFLAG_END;
else
sc->ti_rdata->ti_tx_ring[cur].ti_flags |= TI_BDFLAG_END;
sc->ti_cdata.ti_tx_chain[cur] = m_head;
@@ -2211,6 +2994,7 @@ static int ti_ifmedia_upd(ifp)
struct ti_softc *sc;
struct ifmedia *ifm;
struct ti_cmd_desc cmd;
+ u_int32_t flowctl;
sc = ifp->if_softc;
ifm = &sc->ifmedia;
@@ -2218,21 +3002,52 @@ static int ti_ifmedia_upd(ifp)
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
return(EINVAL);
+ flowctl = 0;
+
switch(IFM_SUBTYPE(ifm->ifm_media)) {
case IFM_AUTO:
+ /*
+ * Transmit flow control doesn't work on the Tigon 1.
+ */
+ flowctl = TI_GLNK_RX_FLOWCTL_Y;
+
+ /*
+ * Transmit flow control can also cause problems on the
+ * Tigon 2, apparantly with both the copper and fiber
+ * boards. The symptom is that the interface will just
+ * hang. This was reproduced with Alteon 180 switches.
+ */
+#if 0
+ if (sc->ti_hwrev != TI_HWREV_TIGON)
+ flowctl |= TI_GLNK_TX_FLOWCTL_Y;
+#endif
+
CSR_WRITE_4(sc, TI_GCR_GLINK, TI_GLNK_PREF|TI_GLNK_1000MB|
- TI_GLNK_FULL_DUPLEX|TI_GLNK_RX_FLOWCTL_Y|
+ TI_GLNK_FULL_DUPLEX| flowctl |
TI_GLNK_AUTONEGENB|TI_GLNK_ENB);
+
+ flowctl = TI_LNK_RX_FLOWCTL_Y;
+#if 0
+ if (sc->ti_hwrev != TI_HWREV_TIGON)
+ flowctl |= TI_LNK_TX_FLOWCTL_Y;
+#endif
+
CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_100MB|TI_LNK_10MB|
- TI_LNK_FULL_DUPLEX|TI_LNK_HALF_DUPLEX|
+ TI_LNK_FULL_DUPLEX|TI_LNK_HALF_DUPLEX| flowctl |
TI_LNK_AUTONEGENB|TI_LNK_ENB);
TI_DO_CMD(TI_CMD_LINK_NEGOTIATION,
TI_CMD_CODE_NEGOTIATE_BOTH, 0);
break;
case IFM_1000_SX:
case IFM_1000_T:
+ flowctl = TI_GLNK_RX_FLOWCTL_Y;
+#if 0
+ if (sc->ti_hwrev != TI_HWREV_TIGON)
+ flowctl |= TI_GLNK_TX_FLOWCTL_Y;
+#endif
+
CSR_WRITE_4(sc, TI_GCR_GLINK, TI_GLNK_PREF|TI_GLNK_1000MB|
- TI_GLNK_RX_FLOWCTL_Y|TI_GLNK_ENB);
+ flowctl |TI_GLNK_ENB);
CSR_WRITE_4(sc, TI_GCR_LINK, 0);
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
TI_SETBIT(sc, TI_GCR_GLINK, TI_GLNK_FULL_DUPLEX);
@@ -2244,8 +3059,14 @@ static int ti_ifmedia_upd(ifp)
case IFM_10_FL:
case IFM_100_TX:
case IFM_10_T:
+ flowctl = TI_LNK_RX_FLOWCTL_Y;
+#if 0
+ if (sc->ti_hwrev != TI_HWREV_TIGON)
+ flowctl |= TI_LNK_TX_FLOWCTL_Y;
+#endif
+
CSR_WRITE_4(sc, TI_GCR_GLINK, 0);
- CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_ENB|TI_LNK_PREF);
+ CSR_WRITE_4(sc, TI_GCR_LINK, TI_LNK_ENB|TI_LNK_PREF|flowctl);
if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_FX ||
IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
TI_SETBIT(sc, TI_GCR_LINK, TI_LNK_100MB);
@@ -2405,6 +3226,313 @@ static int ti_ioctl(ifp, command, data)
return(error);
}
+static int
+ti_open(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ int unit;
+ struct ti_softc *sc;
+
+ unit = minor(dev) & 0xff;
+
+ sc = ti_lookup_softc(unit);
+
+ if (sc == NULL)
+ return(ENODEV);
+
+ TI_LOCK(sc);
+ sc->ti_flags |= TI_FLAG_DEBUGING;
+ TI_UNLOCK(sc);
+
+ return(0);
+}
+
+static int
+ti_close(dev_t dev, int flag, int fmt, struct thread *td)
+{
+ int unit;
+ struct ti_softc *sc;
+
+ unit = minor(dev) & 0xff;
+
+ sc = ti_lookup_softc(unit);
+
+ if (sc == NULL)
+ return(ENODEV);
+
+ TI_LOCK(sc);
+ sc->ti_flags &= ~TI_FLAG_DEBUGING;
+ TI_UNLOCK(sc);
+
+ return(0);
+}
+
+/*
+ * This ioctl routine goes along with the Tigon character device.
+ */
+static int
+ti_ioctl2(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
+{
+ int unit, error;
+ struct ti_softc *sc;
+
+ unit = minor(dev) & 0xff;
+
+ sc = ti_lookup_softc(unit);
+
+ if (sc == NULL)
+ return(ENODEV);
+
+ error = 0;
+
+ switch(cmd) {
+ case TIIOCGETSTATS:
+ {
+ struct ti_stats *outstats;
+
+ outstats = (struct ti_stats *)addr;
+
+ bcopy(&sc->ti_rdata->ti_info.ti_stats, outstats,
+ sizeof(struct ti_stats));
+ break;
+ }
+ case TIIOCGETPARAMS:
+ {
+ struct ti_params *params;
+
+ params = (struct ti_params *)addr;
+
+ params->ti_stat_ticks = sc->ti_stat_ticks;
+ params->ti_rx_coal_ticks = sc->ti_rx_coal_ticks;
+ params->ti_tx_coal_ticks = sc->ti_tx_coal_ticks;
+ params->ti_rx_max_coal_bds = sc->ti_rx_max_coal_bds;
+ params->ti_tx_max_coal_bds = sc->ti_tx_max_coal_bds;
+ params->ti_tx_buf_ratio = sc->ti_tx_buf_ratio;
+ params->param_mask = TI_PARAM_ALL;
+
+ error = 0;
+
+ break;
+ }
+ case TIIOCSETPARAMS:
+ {
+ struct ti_params *params;
+
+ params = (struct ti_params *)addr;
+
+ if (params->param_mask & TI_PARAM_STAT_TICKS) {
+ sc->ti_stat_ticks = params->ti_stat_ticks;
+ CSR_WRITE_4(sc, TI_GCR_STAT_TICKS, sc->ti_stat_ticks);
+ }
+
+ if (params->param_mask & TI_PARAM_RX_COAL_TICKS) {
+ sc->ti_rx_coal_ticks = params->ti_rx_coal_ticks;
+ CSR_WRITE_4(sc, TI_GCR_RX_COAL_TICKS,
+ sc->ti_rx_coal_ticks);
+ }
+
+ if (params->param_mask & TI_PARAM_TX_COAL_TICKS) {
+ sc->ti_tx_coal_ticks = params->ti_tx_coal_ticks;
+ CSR_WRITE_4(sc, TI_GCR_TX_COAL_TICKS,
+ sc->ti_tx_coal_ticks);
+ }
+
+ if (params->param_mask & TI_PARAM_RX_COAL_BDS) {
+ sc->ti_rx_max_coal_bds = params->ti_rx_max_coal_bds;
+ CSR_WRITE_4(sc, TI_GCR_RX_MAX_COAL_BD,
+ sc->ti_rx_max_coal_bds);
+ }
+
+ if (params->param_mask & TI_PARAM_TX_COAL_BDS) {
+ sc->ti_tx_max_coal_bds = params->ti_tx_max_coal_bds;
+ CSR_WRITE_4(sc, TI_GCR_TX_MAX_COAL_BD,
+ sc->ti_tx_max_coal_bds);
+ }
+
+ if (params->param_mask & TI_PARAM_TX_BUF_RATIO) {
+ sc->ti_tx_buf_ratio = params->ti_tx_buf_ratio;
+ CSR_WRITE_4(sc, TI_GCR_TX_BUFFER_RATIO,
+ sc->ti_tx_buf_ratio);
+ }
+
+ error = 0;
+
+ break;
+ }
+ case TIIOCSETTRACE: {
+ ti_trace_type trace_type;
+
+ trace_type = *(ti_trace_type *)addr;
+
+ /*
+ * Set tracing to whatever the user asked for. Setting
+ * this register to 0 should have the effect of disabling
+ * tracing.
+ */
+ CSR_WRITE_4(sc, TI_GCR_NIC_TRACING, trace_type);
+
+ error = 0;
+
+ break;
+ }
+ case TIIOCGETTRACE: {
+ struct ti_trace_buf *trace_buf;
+ u_int32_t trace_start, cur_trace_ptr, trace_len;
+
+ trace_buf = (struct ti_trace_buf *)addr;
+
+ trace_start = CSR_READ_4(sc, TI_GCR_NICTRACE_START);
+ cur_trace_ptr = CSR_READ_4(sc, TI_GCR_NICTRACE_PTR);
+ trace_len = CSR_READ_4(sc, TI_GCR_NICTRACE_LEN);
+
+#if 0
+ printf("ti%d: trace_start = %#x, cur_trace_ptr = %#x, "
+ "trace_len = %d\n", sc->ti_unit, trace_start,
+ cur_trace_ptr, trace_len);
+ printf("ti%d: trace_buf->buf_len = %d\n", sc->ti_unit,
+ trace_buf->buf_len);
+#endif
+
+ error = ti_copy_mem(sc, trace_start, min(trace_len,
+ trace_buf->buf_len),
+ (caddr_t)trace_buf->buf, 1, 1);
+
+ if (error == 0) {
+ trace_buf->fill_len = min(trace_len,
+ trace_buf->buf_len);
+ if (cur_trace_ptr < trace_start)
+ trace_buf->cur_trace_ptr =
+ trace_start - cur_trace_ptr;
+ else
+ trace_buf->cur_trace_ptr =
+ cur_trace_ptr - trace_start;
+ } else
+ trace_buf->fill_len = 0;
+
+
+ break;
+ }
+
+ /*
+ * For debugging, five ioctls are needed:
+ * ALT_ATTACH
+ * ALT_READ_TG_REG
+ * ALT_WRITE_TG_REG
+ * ALT_READ_TG_MEM
+ * ALT_WRITE_TG_MEM
+ */
+ case ALT_ATTACH:
+ /*
+ * From what I can tell, Alteon's Solaris Tigon driver
+ * only has one character device, so you have to attach
+ * to the Tigon board you're interested in. This seems
+ * like a not-so-good way to do things, since unless you
+ * subsequently specify the unit number of the device
+ * you're interested in in every ioctl, you'll only be
+ * able to debug one board at a time.
+ */
+ error = 0;
+ break;
+ case ALT_READ_TG_MEM:
+ case ALT_WRITE_TG_MEM:
+ {
+ struct tg_mem *mem_param;
+ u_int32_t sram_end, scratch_end;
+
+ mem_param = (struct tg_mem *)addr;
+
+ if (sc->ti_hwrev == TI_HWREV_TIGON) {
+ sram_end = TI_END_SRAM_I;
+ scratch_end = TI_END_SCRATCH_I;
+ } else {
+ sram_end = TI_END_SRAM_II;
+ scratch_end = TI_END_SCRATCH_II;
+ }
+
+ /*
+ * For now, we'll only handle accessing regular SRAM,
+ * nothing else.
+ */
+ if ((mem_param->tgAddr >= TI_BEG_SRAM)
+ && ((mem_param->tgAddr + mem_param->len) <= sram_end)) {
+ /*
+ * In this instance, we always copy to/from user
+ * space, so the user space argument is set to 1.
+ */
+ error = ti_copy_mem(sc, mem_param->tgAddr,
+ mem_param->len,
+ mem_param->userAddr, 1,
+ (cmd == ALT_READ_TG_MEM) ? 1 : 0);
+ } else if ((mem_param->tgAddr >= TI_BEG_SCRATCH)
+ && (mem_param->tgAddr <= scratch_end)) {
+ error = ti_copy_scratch(sc, mem_param->tgAddr,
+ mem_param->len,
+ mem_param->userAddr, 1,
+ (cmd == ALT_READ_TG_MEM) ?
+ 1 : 0, TI_PROCESSOR_A);
+ } else if ((mem_param->tgAddr >= TI_BEG_SCRATCH_B_DEBUG)
+ && (mem_param->tgAddr <= TI_BEG_SCRATCH_B_DEBUG)) {
+ if (sc->ti_hwrev == TI_HWREV_TIGON) {
+ printf("ti%d: invalid memory range for "
+ "Tigon I\n", sc->ti_unit);
+ error = EINVAL;
+ break;
+ }
+ error = ti_copy_scratch(sc, mem_param->tgAddr -
+ TI_SCRATCH_DEBUG_OFF,
+ mem_param->len,
+ mem_param->userAddr, 1,
+ (cmd == ALT_READ_TG_MEM) ?
+ 1 : 0, TI_PROCESSOR_B);
+ } else {
+ printf("ti%d: memory address %#x len %d is out of "
+ "supported range\n", sc->ti_unit,
+ mem_param->tgAddr, mem_param->len);
+ error = EINVAL;
+ }
+
+ break;
+ }
+ case ALT_READ_TG_REG:
+ case ALT_WRITE_TG_REG:
+ {
+ struct tg_reg *regs;
+ u_int32_t tmpval;
+
+ regs = (struct tg_reg *)addr;
+
+ /*
+ * Make sure the address in question isn't out of range.
+ */
+ if (regs->addr > TI_REG_MAX) {
+ error = EINVAL;
+ break;
+ }
+ if (cmd == ALT_READ_TG_REG) {
+ bus_space_read_region_4(sc->ti_btag, sc->ti_bhandle,
+ regs->addr, &tmpval, 1);
+ regs->data = ntohl(tmpval);
+#if 0
+ if ((regs->addr == TI_CPU_STATE)
+ || (regs->addr == TI_CPU_CTL_B)) {
+ printf("ti%d: register %#x = %#x\n",
+ sc->ti_unit, regs->addr, tmpval);
+ }
+#endif
+ } else {
+ tmpval = htonl(regs->data);
+ bus_space_write_region_4(sc->ti_btag, sc->ti_bhandle,
+ regs->addr, &tmpval, 1);
+ }
+
+ break;
+ }
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return(error);
+}
+
static void ti_watchdog(ifp)
struct ifnet *ifp;
{
@@ -2413,6 +3541,16 @@ static void ti_watchdog(ifp)
sc = ifp->if_softc;
TI_LOCK(sc);
+ /*
+ * When we're debugging, the chip is often stopped for long periods
+ * of time, and that would normally cause the watchdog timer to fire.
+ * Since that impedes debugging, we don't want to do that.
+ */
+ if (sc->ti_flags & TI_FLAG_DEBUGING) {
+ TI_UNLOCK(sc);
+ return;
+ }
+
printf("ti%d: watchdog timeout -- resetting\n", sc->ti_unit);
ti_stop(sc);
ti_init(sc);
OpenPOWER on IntegriCloud