diff options
Diffstat (limited to 'sys/pci/if_ti.c')
-rw-r--r-- | sys/pci/if_ti.c | 1168 |
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); |