/* * Compaq Tru64 UNIX TULIP Ethernet Network Interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Each interface is referenced by a network interface structure, * is_if, which the routing code uses to locate the interface. */ #define is_if is_ac.ac_if /* network-visible interface */ #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ /* * Limits for the transmit and receive ring entries/descriptors. */ #define TU_NRRING 16 #define TU_NTRING 16 #define TU_TRING_SIZE (TU_NTRING * sizeof(TUDESC)) #define TU_RRING_SIZE (TU_NRRING * sizeof(TUDESC)) /* * Definitions for Multicast and setup frames */ #define TU_PMULTI 14 /* Max perfect multicast addresses */ #define TU_PTOTAL 16 /* Total # perfect filtered addrs */ #define TU_SETUP_SIZE 192 /* Size of Setup Frame */ struct tu_setup { u_char tu_setup_char[12]; }; struct tuflags { u_int aui_mode: 1; /* Set if AUI-10Base5 selected */ u_int bnc_mode: 1; /* Set if BNC-10Base2 selected */ u_int fast_speed: 1; /* Set if speed = 100 Mb/sec */ u_int full_duplex: 1; /* Set if Full-Duplex Enabled */ u_int auto_tr: 1; /* Set if Tx Threshold in auto mode */ u_int sf_mode: 1; /* Set if Tx Store-Forward mode is on */ u_int timer_on: 1; /* Set if soft timer is running */ u_int auto_enabled: 1; /* Set if auto negotiation is enabled */ u_int auto_capable: 1; /* Set if auto negotiation capable */ u_int dc21140: 1; /* Set if DECchip 21140 */ u_int dc21142: 1; /* Set if DECchip 21142 */ u_int dc21143: 1; /* Set if DECchip 21143 */ u_int fx_mode: 1; /* Set if 100BaseFX */ }; /* * Struct for creating init values for SIA Registers for various ports * across all the chip variants */ struct tu_sia_init_data { u_short csr13[5]; u_short csr14[5]; u_short csr15[5]; }; struct tu_softc { struct ether_driver *is_ed; /* Ethernet driver common part */ #define is_ac is_ed->ess_ac /* Ethernet common part */ #define ctrblk is_ed->ess_ctrblk /* Counter block */ #define ztime is_ed->ess_ztime /* Time counters last zeroed */ TUDESC *rring; /* Receive ring desc. addresses */ TUDESC *tring; /* Transmit ring desc. addresses*/ sglist_t rring_sglp; /* Receive ring sglist ptr */ sglist_t tring_sglp; /* Tranmsit ring sglist ptr */ sglist_t tsglp[TU_NTRING]; /* Transmit dma mapping sglist ptrs */ sglist_t rsglp[TU_NRRING]; /* Receive dma mapping sglist ptrs */ struct mbuf *smbuf[TU_NTRING]; /* Saved transmit mbuf chains */ struct mbuf *rmbuf[TU_NRRING]; /* Receive mbufs */ struct tu_setup *setup_bufr; /* Buffer for setup-frame */ struct controller *tu_ctlr; /* Controller reference */ int ntinuse; /* Transmit descriptors in use */ int ntpendg; /* Transmits pending in the ring*/ int itindex; /* Next input transmit index */ int otindex; /* Next output Transmit index */ int rindex; /* Next in/out Receive index */ int tu_sysid_type; /* TULIP SysId Type */ u_int tu_revision; /* TULIP Revision number */ u_int tu_vendor_id; /* TULIP Vendor-ID (DEC) */ u_int tu_device_id; /* TULIP Device-ID (Tulip!) */ u_long tu_csr_base; /* TULIP CSR base value */ u_long tu_csr[16]; /* TULIP CSRs 0..15 io-handles */ u_long tu_cfid; /* TULIP Conf Id Reg io-handle */ u_long tu_cfcs; /* TULIP Conf Cmd/Stat io-handle*/ u_long tu_cfrv; /* TULIP Conf Rev Reg io-handle */ u_long tu_cfcl; /* TULIP Conf Cache Line Size */ u_long tu_cbio; /* TULIP Conf BaseIO io-handle */ u_long tu_cfda; /* TULIP Conf DrvArea io-handle */ struct tuflags tu_flags; /* Flags galore: see tuflags def*/ u_int tu_tr; /* Tx Threshold value selected */ u_int actual_ccount; /* Actual collision count */ u_int ec_reque_count; /* # of Exessive collisions reque'd */ char tu_port_selection[80]; /* Set by media sensing routine */ u_char tu_rom_addr[6]; /* Enet ROM Address saved here */ simple_lock_data_t lk_tu_softc; /* SMP lock for tu_softc */ struct lan_multi is_multi; /* Multicast address list */ ihandler_id_t *hid; /* ihander_id_t rtn from handler_add */ u_char mii_phyaddr; /* Address (0-31) of PHY device */ int tu_auto_state; /* Current state of Auto-Negotiation */ int tu_auto_ticks; /* Time in current state (tick=500ms)*/ u_short tu_auto_ability; /* Our ability that we advertise */ u_char tu_srom_data[128]; /* Copy of SROM */ u_char *tu_srom_leaf_info; /* Pointer to leaf0 info in SROM */ struct tu_sia_init_data *tu_sia_init; struct net_hw_mgmt ehm; /* Enhanced Hw Mgmt Support */ int tu_setup_frame_reset; /* True if reset called from setup_f */ thread_t intr_work_thread; /* Interrupt worker thread ID */ int intr_work_flag; /* For sync with intr worker thread */ }; #define tring_phys tring_sglp->sgp[0].ba #define rring_phys rring_sglp->sgp[0].ba /* * Function prototype definitions for all exported functions. */ int tu_configure(cfg_op_t, cfg_attr_t *, size_t, cfg_attr_t *, size_t); /* * Function prototype definitions for all internal functions (in order * of appearance). */ static int tu_start(struct ifnet *); static void tu_shutdown(struct controller *); static int tuprobe(io_handle_t, struct controller *); static void tu_chip_reset(struct controller *); static void tu_special_chip_reset(struct controller *); static int tu_read_enet_id(struct controller *); static int tu_serialRomCRC(u_char *); static int tu_reset_phy(struct controller *); static int tu_init_rings(struct controller *); static int tu_console_mode(struct controller *); static u_short tu_srom_word(u_char **); static u_int tu_srom_cmdcsr(struct tu_softc *, u_char *); static void tu_set_mii_phy(struct tu_softc *, u_char); static u_int tu_block_type_0(struct tu_softc *, u_char *, u_int); static void tu_block_type_1(struct tu_softc *, u_char *, u_int); static u_int tu_block_type_2(struct tu_softc *, u_char *); static void tu_block_type_3(struct tu_softc *, u_char *); static u_int tu_block_type_4(struct tu_softc *, u_char *); static void tu_block_type_5(struct tu_softc *, u_char *); static u_int tu_use_srom_info(struct tu_softc *, u_char); static u_int tu_select_port(struct tu_softc *, int); static int tuattach(struct controller *); static int tu_unattach(struct bus *, struct controller *); static int tuinit(int); static int tustart(struct ifnet *); static int tu_put_transmit(struct tu_softc *, struct mbuf *); static int tuwatch(int); static void tureset(int); static int tuioctl(struct ifnet *, u_int, caddr_t); static int tu_set_ifchar(int, int, int, int, int); static int tuintr(int); static void tu_intr_work_thread(struct tu_softc *); static void tu_system_error(struct tu_softc *, u_int); static void tu_transmit_int(struct tu_softc *); static void tu_transmit_errors(struct tu_softc *, int); static void tu_receive_int(struct tu_softc *); static int tu_frame_check(u_char *, struct tu_softc *); static void tu_receive_errors(struct tu_softc *, int); static void tu_receive_overflow(struct tu_softc *); static int tu_stop_receives(struct tu_softc *); static int tu_stop_transmits(struct tu_softc *); static int tu_setup_frame(struct controller *); static int tu_put_setup(struct tu_softc *, vm_offset_t, volatile TUDESC **, int); static void tu_setup_addr(u_char *, struct tu_setup *); static void tu_setup_hash(u_char *, u_int *); static int tu_dma_load(long, vm_offset_t, struct controller *, sglist_t *); static u_long tu_getfrom_mii(struct tu_softc *, u_long); static void tu_sendto_mii(struct tu_softc *, u_long, u_long); static u_long tu_mii_rdata(struct tu_softc *); static void tu_mii_wdata(struct tu_softc *, u_long, u_long); static u_long tu_mii_swap(u_long, u_long); static void tu_mii_turnaround(struct tu_softc *, u_long); static u_short tu_mii_rd(struct tu_softc *, u_char); static void tu_mii_wr(struct tu_softc *, u_char, u_long); static int tu_flags_to_port(struct tu_softc *); static void tu_flags_to_ability(struct tu_softc *); static void tu_ability_to_flags(struct tu_softc *, u_short); static void tu_auto_negotiate(struct tu_softc *); static void tu_freemem(int); /* * All externally declared data */ decl_simple_lock_info(extern, tu_softc_lockinfo); extern struct timeval time; /* * All exported (global) data */ #define MAX_TU 32 struct tu_softc *tu_softc[MAX_TU] = {0}; struct controller *tuinfo[MAX_TU] = {0}; /* * Run-time control for transmit and receive error messages */ u_long tu_error_printfs = 0; /* * All private (static) data */ static struct driver tudriver = { tuprobe, 0, tuattach, 0, 0, 0, 0, 0, "tu", tuinfo, 0, 0, 0, 0, 0, tu_unattach, 0 }; /* * Macros and other defines */ /* * Macros for reading and writing CSRs. */ #define TU_RCSR8(csr) READ_BUS_D8(csr) #define TU_RCSR16(csr) READ_BUS_D16(csr) #define TU_RCSR32(csr) READ_BUS_D32(csr) #define TU_WCSR8(csr,value) mb(); WRITE_BUS_D8(csr,(value)); #define TU_WCSR16(csr,value) mb(); WRITE_BUS_D16(csr,(value)); #define TU_WCSR32(csr,value) mb(); WRITE_BUS_D32(csr,(value)); /* * This is used to run our "watchdog" routine on a periodic basis. * The watchdog routine is where we detect timeouts on transmission. */ #define TU_WATCH_FREQUENCY 5 /* in seconds */ /* * Convenient macros for pointing to the next receive and transmit * ring entries. */ #define NEXT_TINDEX(i) (i++, i %= TU_NTRING) #define NEXT_RINDEX(i) (i++, i %= TU_NRRING) /* * Macros to initialize the transmit and receive descriptors: * - Ownership is changed to TULIP in the transmit case when we use the * entry at the time of transmitting or setup frame activation. * - The End-of-Ring control bit is preserved. */ #define INIT_TDESC(dp) {\ ((TUDESC*)dp)->tu_status = 0;\ ((TUDESC*)dp)->tu_bfaddr2 = 0;\ ((TUDESC*)dp)->tu_bfsize2 = 0;\ ((TUDESC*)dp)->tu_control &= TU_TER;\ mb();\ ((TUDESC*)dp)->tu_own = TU_HOST;\ } #define INIT_RDESC(dp) {\ ((TUDESC*)dp)->tu_rflen = 0;\ ((TUDESC*)dp)->tu_status = 0;\ ((TUDESC*)dp)->tu_control &= TU_RER;\ mb();\ ((TUDESC*)dp)->tu_own = TU_TULIP;\ } /* * Accurate max sizes for the transmit and receive buffers: Used in the * DMA mapping routine calls. */ #define MAX_TBUF_SIZE (ETHERMTU + sizeof(struct ether_header)) #define MAX_RBUF_SIZE (ETHERMTU + sizeof(struct ether_header) + 4) /* * Macro to increment x up to a maximum of m: */ #define INCR(x,m) ((x != (unsigned) m) ? x++ : m) /* * Macro to get a 2k cluster mbuf */ #define TUMCLGET(m, wait_flag) { \ MGETHDR((m), wait_flag, MT_DATA); \ if ((m)) { \ MCLGET2((m), wait_flag); \ if (((m)->m_flags & M_EXT) == 0) { \ m_freem((m)); \ (m) = (struct mbuf *)NULL; \ } \ } \ } /* * Following codes are used by the SRM console to communicate selection of * the "EW*0_MODE" environment variable. */ #define TU_PORT_AUI 0x01 #define TU_PORT_BNC 0x02 #define TU_PORT_UTP 0x03 #define TU_PORT_FDX 0x04 #define TU_PORT_100 0x05 #define TU_PORT_100F 0x06 #define TU_PORT_100FX 0x07 #define TU_PORT_100FXF 0x08 #define TU_PORT_AUTO 0x10 /* * Following are defined by the DECchip 21x4y SROM specification. */ #define TU_SROM_UTP 0x00 #define TU_SROM_BNC 0x01 #define TU_SROM_AUI 0x02 #define TU_SROM_100 0x03 #define TU_SROM_FDX 0x04 #define TU_SROM_100F 0x05 #define TU_SROM_UNKN 0x06 /* This is currently unknown */ #define TU_SROM_100FX 0x07 /* 100BaseFX Half Duplex */ #define TU_SROM_100FXF 0x08 /* 100BaseFX Full Duplex */ /* * Little table to map console port values to srom media types. */ static u_char tu_srom_media_code[] = {-1, TU_SROM_AUI, TU_SROM_BNC, TU_SROM_UTP, TU_SROM_FDX, TU_SROM_100, TU_SROM_100F, TU_SROM_100FX, TU_SROM_100FXF }; /* * States for our little auto negotiation state machine: */ enum auto_negotiation_states { TU_AUTO_STATE_INITIAL, TU_AUTO_STATE_RESET, TU_AUTO_STATE_RESTART, TU_AUTO_STATE_PENDING, TU_AUTO_STATE_LINK_UP }; /* * Save some cycles: we really don't need to do unloads */ #define dma_map_unload /* * Configuration data * ================== */ static unsigned char bus_optiondata[512] = ""; static int tu_configured = 0; static int tu_sf_mode = 0; static struct lan_config_data tu_data = { LAN_CONFIG_VERSION_ID, 0, &tudriver, &tu_configured }; cfg_subsys_attr_t tu_attributes[] = { /* * TU driver supports the PCI bus */ {"PCI_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_HIDDEN_ATTR, (caddr_t)bus_optiondata, 0, 512, 0}, /* * Parameter for putting ALL adapters into store and forward * mode. This is for those systems that have a busy I/O bus. */ {"StoreAndForward", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE, (caddr_t)&tu_sf_mode, 0, 1, 0}, /* * Mark the end of the list. */ {"", 0, 0, 0, 0, 0, 0} }; /* * tu_configure() * Configures, Unconfigures, and returns status based on requests * from cfgmgr. * * Arguments: * op Operation type (CFG_OP_CONFIGURE, CFG_OP_UNCONFIGURE, * CFG_OP_QUERY). * indata Input data structure * indatalen Number of cfg_attr_t entries * * Return Value: * 0 success * EINVAL Invalid parameter * ENOPEN not applicable to LAN drivers * EBUSY Device is active * ESRCH not applicable to LAN drivers */ int tu_configure(cfg_op_t op, /* Operation */ cfg_attr_t *indata, /* Input data structure */ size_t indatalen, /* Size of input data structure */ cfg_attr_t *outdata, /* Output data structure */ size_t outdatalen) /* Size of output data structure */ { return (lan_configure(op, &tu_data)); } /* * SIA (CSR13-15) values for selecting AUI, BNC, and UTP half- and full-duplex. * * UTP BNC AUI unused FDX * | | | | | * v v v v v */ static struct tu_sia_init_data tu_21142_sia_data = { {0x0001, 0x0009, 0x0009, 0x0000, 0x0001}, /* <---- CSR13 */ {0x7F3F, 0x0705, 0x0705, 0x0000, 0x7F3D}, /* <---- CSR14 */ {0x0008, 0x0006, 0x000E, 0x0000, 0x0008} /* <---- CSR15 */ }; static int tu_start(struct ifnet *ifp) { register struct tu_softc *sc = tu_softc[ifp->if_unit]; int s = splimp(); /* * If we're already busy transmitting or processing completions (in * the interrupt routine), there's no point in blocking the caller. * The interface queue will be examined and serviced anyway. */ if (simple_lock_try(&sc->lk_tu_softc)) { tustart(ifp); simple_unlock(&sc->lk_tu_softc); } splx(s); return(0); } /* * This little routine is used for registering a shutdown routine for Tulip. * All we need to do is reset the chip. And in the case where we have an * MII Phy we additionally need to reset the PHY chip. Finally we should * also turn off the timer used during auto-negotiation, if any. */ static void tu_shutdown(struct controller *ctlr) { struct tu_softc *sc = tu_softc[ctlr->ctlr_num]; sc->is_if.if_flags &= ~IFF_RUNNING; tu_chip_reset(ctlr); if (sc->tu_flags.timer_on) untimeout((void(*)(void *))tu_auto_negotiate, sc); if (sc->mii_phyaddr) tu_mii_wr(sc, TU_MII_CR, TU_MII_CR_RST); } /* * tuprobe() * Initialize data in the softc structure for the driver * Read the ethernet rom address and also reset the chip * Allocate and initialize transmit and receive rings * * Arguments: * io_handle io-handle for this device as determined by caller. * ctlr pointer to controller struct for the interface. * * Return Value: * Success: ~0 * Failure: 0 */ static int tuprobe(io_handle_t io_handle, struct controller *ctlr) { register int status, unit = ctlr->ctlr_num; register struct tu_softc *sc; register struct pci_config_hdr *pci_cfg_hdr = (void*)io_handle; register vm_offset_t csr_base; struct handler_intr_info tu_intr_info; register u_int i, chip; ihandler_t tu_ihandle; char revstr[NET_SZ_HWREV]; thread_t thread; extern task_t first_task; /* * Make sure we haven't exceeded the maximum number of units that this * driver supports. */ if (unit >= MAX_TU) { printf("tu%d: unit exceeds max supported devices\n", unit); return 0; } /* * Try to allocate another controller so we can support additional * units -- even if this probe fails. */ if (lan_create_controller(&tu_data) != ESUCCESS) { printf("tu%d: WARNING: create_controller failed\n", unit); } /* * Allocate a softc structure for this device. */ MALLOC(sc, void*, sizeof(struct tu_softc), M_DEVBUF, M_WAITOK|M_ZERO); if (!sc) { printf("tu%d probe: failed to get buffer memory for softc\n", unit); return 0; } /* * Before proceeding further, save the controller and softc * pointers in our tables. */ tu_softc[unit] = sc; tuinfo[unit] = ctlr; /* * Allocate an ifnet structure for this device. If one already * exists, it will be returned to us. Upon return, the following * fields will be initialized: if_name, if_unit. And, if this * structure is being reused, if_index will also be initialized * with its old value. All other fields are unknown and must * be initialized. Invoke a common macro to clear all statistics * fields in the ether_driver structure. The remaining fields * will be initialized in the rest of this probe function and the * attach function. */ sc->is_ed = if_alloc ("tu", unit, sizeof(struct ether_driver)); CLEAR_LAN_COUNTERS(sc->is_ed); /* * Allocate and initialize data structure for Enhanced Hardware * Management (ehm) support. */ lan_ehm_init(&sc->ehm, NET_EHM_VERSION_ID); /* * Need this for routines that want to know what the unit # is * before attach is called (where if_unit is setup). */ sc->tu_ctlr = ctlr; /* * Init non-zero flags and variables now. */ if (tu_sf_mode == 0) sc->tu_flags.auto_tr = 1; /* * Allocate a buffer for executing the setup-frame. */ MALLOC(sc->setup_bufr, void*, TU_SETUP_SIZE, M_DEVBUF, M_WAITOK); if (sc->setup_bufr == NULL) { printf("tu%d: failed to get a setup buffer\n", unit); tu_freemem(unit); return 0; } /* * Setup variables used for accessing the Tulip registers. * In the case of PCI, CSR access is based off of the contents * of PCI Configuration Base IO register (CBIO). The PCI bus * support code returns this value via the config header. */ sc->tu_csr_base = csr_base = pci_cfg_hdr->bar1; /* * Setup "absolute" handles for accessing CSRs 0-15. As per * the spec, these CSRs are accessed by taking the above base * and adding/oring in the PCI-TULIP specific offsets. */ for (i=0; i<16; i++) sc->tu_csr[i] = csr_base + (i * 0x08); /* * The PCI Configuration registers are accessed by taking the * configuration base provided in the PCI header and adding in * the appropriate offsets. */ sc->tu_cfid = pci_cfg_hdr->config_base + PCI_VID; sc->tu_cfcs = pci_cfg_hdr->config_base + PCI_COMMAND; sc->tu_cfrv = pci_cfg_hdr->config_base + PCI_REVID; sc->tu_cfcl = pci_cfg_hdr->config_base + PCI_CACHE_LINE_SIZE; sc->tu_cbio = pci_cfg_hdr->config_base + PCI_BASE_REG0; sc->tu_cfda = pci_cfg_hdr->config_base + PCI_DEV_DEP_START; sc->tu_sysid_type = 0; /* * Read and save TULIP's revision, the device id, and * vendor id. */ sc->tu_revision = TU_RCSR32(sc->tu_cfrv) & 0x00FF; sc->tu_vendor_id = TU_RCSR32(sc->tu_cfid) & 0xFFFF; sc->tu_device_id = TU_RCSR32(sc->tu_cfid) >> 16; /* * Reset the chip at this point. Especially since we can have an * interrupt pending (for whatever reason) or in the case of shared * interrupts, we want to make sure the status register indicates * there's nothing for us to do. */ tu_chip_reset(ctlr); /* * Register our interrupt routine. Also declare ourselves as being * capable of sharing interrupts. Note: We'll enable interrupts at * the end of attach. */ tu_intr_info.configuration_st = (caddr_t)ctlr; tu_intr_info.intr = tuintr; tu_intr_info.param = (caddr_t)unit; tu_intr_info.config_type = CONTROLLER_CONFIG_TYPE | SHARED_INTR_CAPABLE | DIST_INT_CLASS_NETWORK; tu_ihandle.ih_bus = ctlr->bus_hd; tu_ihandle.ih_bus_info = (char *)&tu_intr_info; sc->hid = handler_add(&tu_ihandle); if (sc->hid == (ihandler_id_t *)(NULL)) { printf("tu%d: interrupt handler add failed\n", unit); tu_freemem(unit); return 0; } /* * Figure out what device we're dealing with and setup accordingly. */ switch (sc->tu_device_id) { case DEC_21140: chip = 0; sc->tu_flags.dc21140 = 1; lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "21140"); break; case DEC_21142: /* Same ID as the 21143 */ if (sc->tu_revision < 0x20) { chip = 2; sc->tu_flags.dc21142 = 1; lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "21142"); } else { chip = 3; sc->tu_flags.dc21143 = 1; lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "21143"); } sc->tu_sia_init = &tu_21142_sia_data; break; default: printf("tu%d: device not supported [PCI Id=0x%02x]\n", unit, sc->tu_device_id); tu_freemem(unit); return 0; } /* * The method used for selecting the speed and media is through * the SRM Console */ lan_set_attribute(sc->ehm.current_val, NET_METHOD_NDX, net_method_console); /* * The 21143 comes up in sleep mode by default; make sure it's * awakened. */ if (sc->tu_flags.dc21143) { TU_WCSR32(sc->tu_cfda, (TU_RCSR32(sc->tu_cfda) & 0xFF00)); } /* * Display the type of chip and it's revision (as step.step-revision). */ sprintf(revstr, "%d.%d", ((sc->tu_revision & 0xF0) >> 4), (sc->tu_revision & 0xF)); printf("tu%d: DECchip 2114%d: Revision: %s\n", unit, chip, revstr); lan_set_attribute(sc->ehm.current_val, NET_HWREV_NDX, revstr); /* * Check for an MII PHY. Note that this should really be done based * on the whether or not the SROM says if an MII PHY is present. */ if ((status = tu_reset_phy(ctlr)) != ESUCCESS) { tu_freemem(unit); return 0; } /* * For us (at this time), we can auto-negotiate if there's an MII * PHY or we're dealing with a 21143. * * We won't print out if the device is auto-negotiation capable * until we check to make sure it's not a DE500-FA (which uses * a 21143, but is 100BaseFX with no auto-negotiation). */ if (sc->mii_phyaddr || sc->tu_flags.dc21143) { sc->tu_flags.auto_capable = 1; } /* * Create the interrupt processing thread */ if (thread_create(first_task, &thread) != KERN_SUCCESS) { printf("tu%d: Could not create the receive interrupt thread\n", unit); return 0; } else { /* * Set up the thread in a suspended state and pass in the * softc as an argument. */ thread_start(thread, (void(*)())tu_intr_work_thread, THREAD_SYSTEMMODE, (void *)sc); /* Set the thread's priority and mark it as not swappable */ thread->max_priority = thread->priority = thread->sched_pri = (BASEPRI_SYSTEM - 5); thread->ipc_kernel = TRUE; thread_swappable(thread, FALSE); /* Set the scheduling policy. Use a quantum of 100ms. */ (void) thread_policy (thread, POLICY_RR, 100); /* Start the thread running now */ (void) thread_resume (thread); /* Save the thread id so we can terminate it later */ sc->intr_work_thread = thread; } /* * Read and save the Ethernet ROM address. The reading of this Id is * based on how the Tulip is served to us (de425, de435, pass #, etc.) */ if (tu_read_enet_id(ctlr) != ESUCCESS) { tu_freemem(unit); return(0); } /* * Initialize and setup the transmit and receive rings. */ if (tu_init_rings(ctlr) != ESUCCESS) { tu_freemem(unit); return(0); } /* * Pick-up "ew*0_mode" setting from the console and check it out. */ if (tu_console_mode(ctlr) != ESUCCESS) { tu_freemem(unit); return(0); } /* * If we get here, then everything must've been fine. Register our * shutdown routine to halt all Tulip DMA activity when we bring * down the system. */ drvr_register_shutdown(tu_shutdown, (void*)ctlr, DRVR_REGISTER); return(sizeof(struct tu_softc)); } /* * tu_chip_reset() * Do a soft reset of the TULIP chip. * * Argument * ctlr pointer to controller struct for the interface. * * Return value * None. Always succeeds. */ static void tu_chip_reset(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; /* * Soft reset is done by first setting and then clearing the * SWR bit in CSR0. We need to add a little delay when the * reset is held and also after reset is released, as stated * in the specification. */ TU_WCSR32(sc->tu_csr[0], TU_CSR0_SWR); DELAY(50); /* * Release reset and hold on for a few microseconds. */ TU_WCSR32(sc->tu_csr[0], TU_CSR0_2114x); DELAY(50); } static void tu_special_chip_reset(struct controller *ctlr) /* * The 21140 and the 21142 specs tell us to reset the chip if CSR6 will * change state. This routine figures out what the state of this bit will be * and does the reset after writing to CSR6. */ { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; if (sc->tu_flags.dc21140 || sc->tu_flags.dc21142) { /* * If operating at the 100 speed, or we have an MII PHY, then * PS must be set, otherwise it MBZ. */ if (sc->tu_flags.fast_speed || sc->mii_phyaddr) { TU_WCSR32(sc->tu_csr[6], TU_CSR6_PS); } else { TU_WCSR32(sc->tu_csr[6], 0); } tu_chip_reset(ctlr); } } /* * tu_read_enet_id() * Read the Ethernet ROM ID for the device and save it in softc. * * Arguments: * ctlr pointer to controller struct for the interface. * * Return Value: * - EIO * - ESUCCESS */ static int tu_read_enet_id(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; unsigned char *rom_data = &sc->tu_srom_data[0]; register int null_address; register int i, j, k, status, jnk; /* * Ensure that the SROM is Idle: First select CSR9 to work * with the SROM interface. */ TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_SS); /* * Now deliver it enough clocks with Chip Select set. */ for (i=0; i<25; i++) { TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CLK); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); } TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_SS); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); /* * SROM is ready: Loop on all DATA words. */ for (i=0,j=0; j<64; j++) { /* 128 bytes - word at a time */ u_short d,w; /* * Output the READ command to the SROM */ for (k=0; k<2; k++) { TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_DT); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_DT_CLK); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_DT); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); } TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CLK); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); /* * Output the WORD Address of the SROM */ for (k=0; k<6; k++) { d = ((j >> (5-k)) & 1) << 2; TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS | d); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CLK | d); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS | d); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); } /* * Verify that the SROM output data has now become 0. */ if (TU_RCSR32(sc->tu_csr[9]) & TU_CSR9_DO) { printf("tu%d: failed to read SROM Id\n", unit); return EIO; } /* * Input the WORD of data from the SROM */ for (w=0,k=0; k<16; k++) { TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CLK); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); d = TU_RCSR32(sc->tu_csr[9]) & TU_CSR9_DO; w |= ((d >> 3) & 1) << (15-k); TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_CS); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); } rom_data[i++] = w & 0xff; rom_data[i++] = w >> 8; /* * Negate the CS (chip select) to end the SROM command */ TU_WCSR32(sc->tu_csr[9], TU_CSR9_WRITE_SS); jnk = TU_RCSR32(sc->tu_cfrv); DELAY(1); } /* * Make sure we're not dealing with a multi-port module */ if (sc->tu_srom_data[19] > 1) { printf("tu%d: can't support multi-port cards\n", unit); return EIO; } /* * If device is auto-negotiation capable (and not a * DE500-FA), then print out notification. * * The auto_capable flag is already set in tu_softc_init * for appropriate devices. */ if ((sc->mii_phyaddr || sc->tu_flags.dc21143) && !((sc->tu_srom_data[2] == 0x0f) && (sc->tu_srom_data[3] == 0x50))) printf("tu%d: auto negotiation capable device\n", unit); else sc->tu_flags.auto_capable = 0; /* * Figure out and save away pointer to the Controller_0 Leaf * Info. We will be needing this during media/port selection. */ i = (u_short)(sc->tu_srom_data[27] | (sc->tu_srom_data[28]<<8)); sc->tu_srom_leaf_info = &sc->tu_srom_data[i]; /* * Validate the address before we use it. Checksum the entire SROM */ status = tu_serialRomCRC(&rom_data[0]); if (!status) { printf("tu%d: ethernet address checksum error\n",unit); return EIO; } /* * Ethernet address is always at offset 20 (decimal). */ i = 20; /* * Save the address in our softc structure for subsequent use. Check * for an address of all 0s while we're at it. */ for (j=0,null_address=1; j<6; j++,i++) if (sc->is_addr[j] = rom_data[i]) null_address = 0; if (null_address) { printf("tu%d: ethernet address contains all zeroes\n", unit); return EIO; } /* * If we ever get here, then everything must've been OK. Save the * Id in the softc as our "default" physical address. This is used * for certain ioctls. */ bcopy(sc->is_addr, sc->tu_rom_addr, 6); return ESUCCESS; } /* * Computes and verifies the Serial ROM checksum/CRC used on devices based * on the 21041 (DE450) and 2114x chips. This algorithm was taken straight * from the 21140 Hardware Specification. */ static int tu_serialRomCRC(u_char *SromData) { register u_int const POLY = 0x04c11db6; register u_int FlippedCrc = 0, Crc = 0xffffffff; register u_char i, CurrentByte, Bit, Msb; register u_char chksm_1, chksm_2; for (i=0; i<126; i++) { CurrentByte = SromData[i]; for (Bit=0; Bit<8; Bit++) { Msb = (Crc >> 31) & 1; Crc <<= 1; if (Msb ^ (CurrentByte & 1)) { Crc ^= POLY; Crc |= 1; } CurrentByte >>= 1; } } for (i=0; i<32; i++) { FlippedCrc <<= 1; Bit = Crc & 1; Crc >>= 1; FlippedCrc += Bit; } Crc = FlippedCrc ^ 0xffffffff; chksm_1 = Crc & 0xff; chksm_2 = Crc >> 8; /* * CRC word is stored by at the very end of the SROM */ return (SromData[126] == chksm_1) && (SromData[127] == chksm_2); } /* * tu_reset_phy() * Resets the MII PHY device (National DP83840) if one is found. * * Arguments * ctlr pointer to controller struct for the interface. * * Return Value * - EIO if PHY exists but can't reset it * - ESUCCESS if PHY doesn't exist or if it does, reset was OK. */ static int tu_reset_phy(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; u_short phyrev; /* * Check for existance of National Semiconductor MII PHY */ sc->mii_phyaddr = 5; if (tu_mii_rd(sc, TU_MII_ID) != (u_short)0x2000) { sc->mii_phyaddr = 0; return ESUCCESS; } /* * Make sure it's either the 83840 or the 83840A. */ phyrev = tu_mii_rd(sc, TU_MII_ID+1); if ((phyrev != (u_short)0x5c00) && (phyrev != (u_short)0x5c01)) { sc->mii_phyaddr = 0; printf("tu%d: MII PHY is not supported (rev = %04X).\n", unit, phyrev); return EIO; } tu_mii_wr(sc, TU_MII_CR, TU_MII_CR_RST); DELAY(1000000); if (tu_mii_rd(sc, TU_MII_CR) & TU_MII_CR_RST) { printf("tu%d: failed to reset PHY device\n", unit); sc->mii_phyaddr = 0; return EIO; } return ESUCCESS; } /* * tu_init_rings() * Allocate and initialize the transmit and receive rings * and related structs. This is one-time initialization. * * Arguments * ctlr pointer to controller struct for the interface. * * Return Value * - EIO * - ENOMEM * - ESUCCESS */ static int tu_init_rings(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; register void *mem_ptr; register u_int i; /* * Allocate and zero out the buffer for the transmit ring */ MALLOC(mem_ptr, void*, TU_TRING_SIZE, M_DEVBUF, M_WAITOK|M_ZERO); if (mem_ptr == NULL) { printf("tu%d: no buffer for transmit ring\n", unit); return ENOMEM; } sc->tring = mem_ptr; /* * As above for the receive ring buffer */ MALLOC(mem_ptr, void*, TU_RRING_SIZE, M_DEVBUF, M_WAITOK|M_ZERO); if (mem_ptr == NULL) { printf("tu%d: no buffer for receive ring\n", unit); return ENOMEM; } sc->rring = mem_ptr; /* * Set the End-Of-Ring control bits to indicate where our rings end. * These are set once here and never cleared and set again. (Macros * for initializing the descriptors make sure of that.) */ sc->tring[TU_NTRING-1].tu_control = TU_TER; sc->rring[TU_NRRING-1].tu_control = TU_RER; /* * Map the kernel virtual addresses to something that the Tulip can * digest. Note that tring/ring_sglps are set to NULL (up above to * force an alloc prior to the load. */ if ((tu_dma_load(TU_TRING_SIZE, (vm_offset_t)sc->tring, ctlr, &sc->tring_sglp) == 0) || (tu_dma_load(TU_RRING_SIZE, (vm_offset_t)sc->rring, ctlr, &sc->rring_sglp) == 0)) { return EIO; } /* * Allocate the transmit scatter-gather list structures here for the * max possible byte-counts. Also initialize the saved-mbuf pointers. */ for (i=0; itsglp[i] = NULL; if (dma_map_alloc(bc, ctlr, &sc->tsglp[i], flags) != bc) { printf("tu%d: dma mapping [alloc] error\n", unit); return EIO; } sc->smbuf[i] = NULL; } /* * Setup the receive ring: We'll fail initialization if there is a * failure in allocating an mbuf, or there's trouble mapping an mbuf * (data) virtual address to its bus/physical address. We won't bother * recovering any buffers at this point if something goes wrong. */ for (i=0; irring[i]; register vm_offset_t va; register sglist_t sglp; /* * Attempt to get a 2k cluster mbuf: wait if necessary */ TUMCLGET(m, M_WAIT); if (m == NULL) { printf("tu%d: no receive mbufs\n", unit); return ENOMEM; } /* * Map the mbuf data address for Tulip's consumption. */ va = mtod(m, vm_offset_t); sc->rsglp[i] = NULL; /* Alloc before load */ if (tu_dma_load(bc, va, ctlr, &sc->rsglp[i]) == 0) { printf("tu%d: unable to map receive mbuf\n", unit); m_freem(m); return EIO; } sglp = sc->rsglp[i]; /* * Setup the first ba-bc pair in the ring descriptor */ rp->tu_bfaddr1 = (u_int)sglp->sgp[0].ba; rp->tu_bfsize1 = sglp->sgp[0].bc; /* * We don't really expect a page-crossing within an * mbuf, but account for it anyway for future changes. */ if (sglp->val_ents > 1) { rp->tu_bfaddr2 = (u_int)sglp->sgp[1].ba; rp->tu_bfsize2 = sglp->sgp[1].bc; } else { rp->tu_bfaddr2 = 0; rp->tu_bfsize2 = 0; } /* * Save the mbuf reference for the read routine */ sc->rmbuf[i] = m; } /* * All done for now. */ return ESUCCESS; } /* * tu_console_mode() * Picks up the "ew*0_mode" setting done at the console level and * does a very basic check of the selected port. * * Arguments * ctlr pointer to controller struct for the interface. * * Return Value * - ESUCCESS * - EIO */ static int tu_console_mode(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; register char *port = &sc->tu_port_selection[0]; register u_int i, console_mode, cmdcsr; register u_int link_ok=0, no_carrier=0; u_char *buf_ptr = (void*)sc->setup_bufr; volatile TUDESC *tp = &sc->tring[0]; sglist_t sglp = sc->tsglp[0]; /* * If the console firmware running on our platform supports passing * of the "mode" environment variable (via the Config Driver-Area * register), then use that selection; otherwise, default to something. */ console_mode = (TU_RCSR32(sc->tu_cfda) & 0xff00) >> 8; switch (console_mode) { case TU_PORT_AUI: sc->tu_flags.aui_mode = 1; break; case TU_PORT_BNC: sc->tu_flags.bnc_mode = 1; break; case TU_PORT_FDX: sc->tu_flags.full_duplex = 1; case TU_PORT_UTP: break; case TU_PORT_AUTO: sc->tu_flags.auto_enabled = 1; case TU_PORT_100F: sc->tu_flags.full_duplex = 1; case TU_PORT_100: sc->tu_flags.fast_speed = 1; break; case TU_PORT_100FXF: sc->tu_flags.full_duplex = 1; case TU_PORT_100FX: sc->tu_flags.fast_speed = 1; sc->tu_flags.fx_mode = 1; break; default: console_mode = 0; } /* * Some simple sanity checks */ if (sc->tu_flags.auto_enabled && !sc->tu_flags.auto_capable) { printf("tu%d: bad console (ew_)mode selected for device\n", unit); return EIO; } if (!console_mode) { strcpy(port, "no console mode: defaulting to "); console_mode = TU_PORT_UTP; } else if (!sc->tu_flags.auto_capable) { strcpy(port, "console mode: selecting "); } else if (!sc->tu_flags.auto_enabled) { strcpy(port, "auto negotiation off: selecting "); } else { strcpy(port, "auto negotiation on: will advertise "); /* * Initialize the ability field (used to advertise during * auto-negotiation). */ tu_flags_to_ability(sc); link_ok = 1; } /* * Select port (for testing) if we aren't auto-negotiating */ if (!sc->tu_flags.auto_enabled) { tu_special_chip_reset(ctlr); cmdcsr = tu_select_port(sc, console_mode); } /* * Check for a good 100Mbps (UTP) link first. Skip if auto-negotiating. */ if (sc->tu_flags.fast_speed && !sc->tu_flags.auto_enabled) { for (i=0; i<1500; i++) { if ((sc->mii_phyaddr && (!(tu_mii_rd(sc, TU_MII_SR) & TU_MII_SR_LKS))) || (sc->tu_flags.dc21143 && (TU_RCSR32(sc->tu_csr[12]) & TU_CSR12_LS100)) || (sc->tu_flags.dc21140 && !sc->mii_phyaddr && (TU_RCSR32(sc->tu_csr[12]) & 0x40))) { DELAY(1000); } else { link_ok = 1; break; } } } /* * Check for a 10Mbps (UTP) link: As above, skip if auto-negotiating. */ if (!(sc->tu_flags.aui_mode || sc->tu_flags.bnc_mode || sc->tu_flags.fast_speed || sc->tu_flags.auto_enabled)) { u_int link_fail; if (sc->tu_flags.dc21140) link_fail = 0x80; else link_fail = TU_CSR12_LNK; /* * Have to wait upto 2.4 seconds for link-test to complete */ for (i=0; i<2400; i++) { if ((!sc->mii_phyaddr && (TU_RCSR32(sc->tu_csr[12]) & link_fail)) || (sc->mii_phyaddr && (!(tu_mii_rd(sc, TU_MII_SR) & TU_MII_SR_LKS)))) { DELAY(1000); } else { link_ok = 1; break; } } } /* * Wrap up 10 or 100BaseT */ if (!(sc->tu_flags.aui_mode || sc->tu_flags.bnc_mode || sc->tu_flags.fx_mode)) { sprintf(port+strlen(port), "%s (UTP) port: %s duplex", sc->tu_flags.fast_speed ? "100BaseTX" : "10BaseT", sc->tu_flags.full_duplex ? "full" : "half"); lan_set_attribute(sc->ehm.current_val, NET_MEDIA_NDX, lan_media_strings[LAN_MEDIA_UTP]); tu_chip_reset(ctlr); return ESUCCESS; } /* * For FX mode (currently only supporting 100BaseFX), do the * same basic setup as for UTP... */ if (sc->tu_flags.fx_mode) { sprintf(port+strlen(port), "100BaseFX port: %s duplex", sc->tu_flags.full_duplex ? "full" : "half"); lan_set_attribute(sc->ehm.current_val, NET_MEDIA_NDX, lan_media_strings[LAN_MEDIA_FIBER]); tu_chip_reset(ctlr); return ESUCCESS; } /* * For the AUI or BNC ports, we'll send out a test packet to help * us figure out whether or not a cable is in. */ /* * Map the setup-buffer we'll use for sending the test packet */ if (tu_dma_load(64L, (vm_offset_t)buf_ptr, ctlr, &sc->tsglp[0]) == 0) { printf("tu%d: failed to map setup buffer\n", unit); tu_chip_reset(ctlr); return EIO; } /* * Init transmit ring address and command registers */ TU_WCSR32(sc->tu_csr[4], (u_int)sc->tring_phys); TU_WCSR32(sc->tu_csr[6], cmdcsr | TU_CSR6_ST); /* * Create an 802.2 test packet as follows: * * 08 00 2B AA BB CC 08 00 2B AA BB CC 00 03 00 00 E3 * * Where: "00 03" is Length * "00 00" are the Null SAPs, and * "E3" indicates a Test packet */ bcopy(sc->tu_rom_addr, buf_ptr, 6); bcopy(sc->tu_rom_addr, buf_ptr+6, 6); buf_ptr[12] = 0x00; buf_ptr[13] = 0x03; buf_ptr[14] = 0x00; buf_ptr[15] = 0x00; buf_ptr[16] = 0xE3; /* * Setup the transmit descriptor and tell the chip to go: note * that we have "padding" enabled by default via the command * register, so we'll actually send out a 64 byte packet. */ tp->tu_bfaddr1 = (u_int)sglp->sgp[0].ba; tp->tu_bfsize1 = 17; tp->tu_control |= TU_TFS | TU_TLS; mb(); tp->tu_own = TU_TULIP; TU_WCSR32(sc->tu_csr[1], 1); /* * Wait for packet completion or timeout: we need to wait * up to 500 milliseconds to allow for exessive collisions. */ for (i=0; (i < 500) && (tp->tu_own == TU_TULIP); i++) { DELAY(1000); } /* * We go through all of the above trouble for this potential message. */ if (tp->tu_status & (TU_NC | TU_EC | TU_LO)) no_carrier = 1; sprintf(port+strlen(port), "10Base%s port%s", sc->tu_flags.aui_mode ? "5 (AUI)" : "2 (BNC)", no_carrier ? ": no carrier" : ""); lan_set_attribute(sc->ehm.current_val, NET_MEDIA_NDX, sc->tu_flags.aui_mode ? lan_media_strings[LAN_MEDIA_AUI]: lan_media_strings[LAN_MEDIA_BNC]); /* * Clean up before we return. */ dma_map_unload(0, sglp); INIT_TDESC(tp); tu_chip_reset(ctlr); return ESUCCESS; } /* * tu_srom_word() * This routine simply fetches a word (16 bits) from the SROM * image in memory. We need something like this since we do *ptr * and ptr is not always guaranteed to be 16-bit aligned. * * Arguments * srom_data_ptr --- pointer to pointer in SROM where we want to * do the read. Passed by reference so we can bump * it up (by 2). * * Return Value * - the 16-bit word at *srom_data_ptr */ static u_short tu_srom_word(u_char **srom_data_ptr) { u_char *srom_data = (*srom_data_ptr)++; return (u_short) ((*srom_data) | ((*(*srom_data_ptr)++) << 8)); } /* * tu_srom_cmdcsr() * This little routine picks up the PS, TTM, PCS, and SCR bits * in the 'Command' field specified in various SROM blocks and * sets corresponding bits in the CSR6 image that we'll eventually * write. * * Arguments * sc pointer to the softc structure * srom_data pointer to where the command field is in the SROM * * Return Value * - CSR6 (cmdcsr) value with the above bits set/clear as the case may be. */ static u_int tu_srom_cmdcsr(struct tu_softc *sc, u_char *srom_data) { u_int cmdcsr = 0; u_char command = *srom_data; if (command & 0x01) cmdcsr |= TU_CSR6_PS; if (command & 0x10) cmdcsr |= TU_CSR6_TTM; if (command & 0x20) cmdcsr |= TU_CSR6_PCS; if (command & 0x40) cmdcsr |= TU_CSR6_SCR; return cmdcsr; } /* * tu_set_mii_phy() * Simply programs the MII PHY Command register to operate at a * given speed and duplex mode (with auto-negotiation disabled). * * Arguments * sc pointer to the softc structure * media_selected SROM media-code (10/100BaseT, Half/Full-Duplex) * * Return Value * - None */ static void tu_set_mii_phy(struct tu_softc *sc, u_char media_selected) { u_short mii_cr_init; switch (media_selected) { case TU_SROM_UTP: mii_cr_init = TU_MII_CR_10; break; case TU_SROM_FDX: mii_cr_init = TU_MII_CR_10 | TU_MII_CR_FDX; break; case TU_SROM_100: mii_cr_init = TU_MII_CR_100; break; case TU_SROM_100F: mii_cr_init = TU_MII_CR_100 | TU_MII_CR_FDX; break; default: return; } tu_mii_wr(sc, TU_MII_CR, mii_cr_init); } /* * tu_block_type_0() * This routine is used for the 21140 Compact as well as for the * Extended Block Type 0 SROM formats. These formats are pertinent * for the 21140 only when an MII PHY chip is not used. * * Simply need to program the General Purpose Port register (CSR12) * and pick up the PS, TTM, SCR and PCS bits from the Command field * to program later in CSR6. * * Arguments * sc pointer to the softc structure * srom_data pointer to the General Purpose Port Data field * gp_control General Purpose Port Control value to use * * Return Value * - CSR6 (cmdcsr) value with the above bits set/clear as the case may be. */ static u_int tu_block_type_0(struct tu_softc *sc, u_char *srom_data, u_int gp_control) { TU_WCSR32(sc->tu_csr[12], gp_control); TU_WCSR32(sc->tu_csr[12], *srom_data); /* * Pick up the SCR, PCS, TTM, and PS bits. Note that we don't make * use of the following fields within the Command word at this time: * Active_invalid * Default_medium * Polarity */ return tu_srom_cmdcsr(sc, srom_data+1); } /* * tu_block_type_1() * This routine is used for the Extended Block Type 1 SROM format. This * block type is pertinent for the 21140 only when an MII is in use. * * In addition to programming the General Purpose Port register (CSR12) * we need to execute the reset and initialization sequences required * for the PHY, if any. * * Arguments * sc pointer to the softc structure * srom_data pointer to the PHY Number field * gp_control General Purpose Port Control value to use * * Return Value * - None */ static void tu_block_type_1(struct tu_softc *sc, u_char *srom_data, u_int gp_control) { int unit = sc->tu_ctlr->ctlr_num; u_char phy_num = *srom_data++; u_char seq_len = *srom_data++; u_char *seq_ptr = srom_data; u_char *rst_ptr = srom_data + seq_len; u_char rst_len = *rst_ptr++; /* * At this time, we don't support boards with multiple MII PHY chips. */ if (phy_num != 0) { printf("tu%d: can't support MII PHY index %d\n", unit, phy_num); return; } /* * Write the control info first */ TU_WCSR32(sc->tu_csr[12], gp_control); /* * Do the Reset Sequence */ for (; rst_len; rst_len--,rst_ptr++) { TU_WCSR32(sc->tu_csr[12], *rst_ptr); } /* * Next execute the activation and selection sequence, if any. */ for (; seq_len; seq_len--,seq_ptr++) { TU_WCSR32(sc->tu_csr[12], *seq_ptr); } /* * NOTE: We currently don't use and act upon the following fields: * Media Capabilities * Nway Advertisement * FDX Bit Map * TTM Bit Map srom_data = rst_ptr; */ } /* * tu_block_type_2() * This routine is used for the Extended Block Type 2 SROM format (SIA * media). * * SIA media programming simply has to do with setting up the three SIA * registers: CSR13, CSR14, and CSR15. The above formats provide values * for these registers (for a given media) in order to over-ride the * defaults specified in the 21142 and 21143, specs. * * Arguments * sc pointer to the softc structure * srom_data pointer to the Media Specific Data field * * Return Value * - Initial value to use for programming CSR6 */ static u_int tu_block_type_2(struct tu_softc *sc, u_char *srom_data) { u_int csr13, csr14, csr15; u_char media_code = *(srom_data-1) & 0x3f; u_char ext_format = *(srom_data-1) & 0x40; /* * If Extended format, then pick-up values from the SROM data. */ if (ext_format) { csr13 = tu_srom_word(&srom_data); csr14 = tu_srom_word(&srom_data); csr15 = tu_srom_word(&srom_data); } else { /* * Use the default values */ csr13 = sc->tu_sia_init->csr13[media_code]; csr14 = sc->tu_sia_init->csr14[media_code]; csr15 = sc->tu_sia_init->csr15[media_code]; } /* * Do the SIA reset first */ TU_WCSR32(sc->tu_csr[13], 0); /* * Type 1 format, so pick up and program the GPR Control bits * first. Also pick up the GPR data bits now. */ TU_WCSR32(sc->tu_csr[15], csr15|(tu_srom_word(&srom_data)<<16)); csr15 |= (tu_srom_word(&srom_data) << 16); /* * Note that CSR13 reset must be released last */ TU_WCSR32(sc->tu_csr[15], csr15); TU_WCSR32(sc->tu_csr[14], csr14); TU_WCSR32(sc->tu_csr[13], csr13); return 0; } /* * tu_block_type_3() * This routine is used for the Extended Block Type 3 SROM format. This * block type is similar to Type 1 but is used for the newer 21142/3 * chips. * * In addition to programming the General Purpose Port register (upper * 16 bits of CSR13) we need to execute the reset and initialization * sequences required for the MII PHY chip, if any. * * Arguments * sc pointer to the softc structure * srom_data pointer to the PHY Number field * * Return Value * - None */ static void tu_block_type_3(struct tu_softc *sc, u_char *srom_data) { int unit = sc->tu_ctlr->ctlr_num; u_char phy_num = *srom_data++; u_char seq_len = *srom_data++; u_char *seq_ptr = srom_data; u_char *rst_ptr = srom_data + (2*seq_len); u_char rst_len = *rst_ptr++; /* * At this time, we don't support boards with multiple MII PHY chips. */ if (phy_num != 0) { printf("tu%d: can't support MII PHY index %d\n", unit, phy_num); return; } /* * The General Purpose Port control is expected to be the first * word in the Reset sequence, so no seperate action is needed here * as was the case in Type 1. */ for (; rst_len; rst_len--) { TU_WCSR32(sc->tu_csr[15], (tu_srom_word(&rst_ptr) << 16)); } /* * Next execute the activation and selection sequence, if any. */ for (; seq_len; seq_len--) { TU_WCSR32(sc->tu_csr[15], (tu_srom_word(&seq_ptr) << 16)); } /* * NOTE: We currently don't use and act upon the following fields: * Media Capabilities * Nway Advertisement * FDX Bit Map * TTM Bit Map * MII Connector Interrupt srom_data = rst_ptr; */ } /* * tu_block_type_4() * This routine is used for programming SYM media on the 21142/3. * * Simply need to program the General Purpose Port register (upper 16 * bits of CSR15) and pick up the PS, TTM, SCR and PCS bits from the * Command field to program later in CSR6. * * Arguments * sc pointer to the softc structure * srom_data pointer to the General Purpose Port Control field * * Return Value * - CSR6 (cmdcsr) value with the above bits set/clear as the case may be. */ static u_int tu_block_type_4(struct tu_softc *sc, u_char *srom_data) { /* * As the 21142/3, we need to setup CSRs 13,14, and 15 in a * certain way when using the chip in SYM (or MII) mode. So, * ensure that we do that while we program the GPR bits. */ TU_WCSR32(sc->tu_csr[13], 0x0); TU_WCSR32(sc->tu_csr[15], (tu_srom_word(&srom_data)<<16) | 0x8); DELAY(50*1000); /* As per request of NPBU */ TU_WCSR32(sc->tu_csr[15], (tu_srom_word(&srom_data)<<16) | 0x8); TU_WCSR32(sc->tu_csr[14], 0x0); /* * Note that the spec doesn't say we should release SIA reset; as * such, we don't write a 1 into CSR13. */ /* * Pick up the SCR, PCS, TTM, and PS bits. Note that we don't make * use of the following fields within the Command word at this time: * Active_invalid * Default_medium * Polarity */ return tu_srom_cmdcsr(sc, srom_data); } /* * tu_block_type_5() * This routine takes care of Extended Block Type 5. This format can * be used on the 21140, 21142, or the 21143. The task here is to * simply write a certain number of words to the General Purpose Port * register. * * Arguments * sc pointer to the softc structure * srom_data pointer to the Reset Sequence Length field * * Return Value * - */ static void tu_block_type_5(struct tu_softc *sc, u_char *srom_data) { u_char rst_len = *srom_data++; u_char *rst_ptr = srom_data; /* * The GPR is CSR12 in the case of the 21140 and for the 21142/3, * it's the upper 16 bits of CSR15. */ for (; rst_len; rst_len--) { if (sc->tu_flags.dc21140) { TU_WCSR32(sc->tu_csr[12], tu_srom_word(&rst_ptr)); } else { TU_WCSR32(sc->tu_csr[15], (tu_srom_word(&rst_ptr)<<16)); } } } /* * tu_use_srom_info() * This routine uses information read from the SROM to figure out how * the SIA, GPR, and/or MII registers should be programmed for a given * port/media selection. * * Arguments * sc pointer to the softc structure * media_selected A subset of the Media Codes defined in the SROM Spec. * Currently limited to: 10Base2 (BNC) * 10Base5 (AUI) * 10BaseT Half-Duplex * 10BaseT Full-Duplex * 100BaseTX Half-Duplex * 100BaseTX Full-Duplex * Return Value * - CSR6 (cmdcsr) value with the SCR, TTM, PCS, and PS bits set/clear * as the case may be for the selected media. */ static u_int tu_use_srom_info(struct tu_softc *sc, u_char media_selected) { u_int cmdcsr, unit = sc->tu_ctlr->ctlr_num, gp_control; u_char *srom_block, *srom_data = sc->tu_srom_leaf_info + 2; u_char block_count, block_length, block_type, media_code, i; u_char *block__type[6]; /* * First of all, if we're dealing with the 21140, then pick-up the * General Purpose Port (register) control info. We also go ahead * and set bit 8 at this time to indicate that this is control info * being programmed (as opposed to data). This control word is used * on all 21140 block types (Compact, Type 0, and Type 1). */ if (sc->tu_flags.dc21140) gp_control = (u_int) (*srom_data++ | 0x100); /* * Init array used for pointers to block-types we will find and use */ for (i=0; i<6; i++) block__type[i] = 0; /* * Walk the entire SROM. We always pick-up Types 1, 3, and 5. We * must find a match with our selected-media since these are media * specific blocks. */ block_count = *srom_data++; for (i=0; imii_phyaddr) { if (block__type[1]) tu_block_type_1(sc, block__type[1], gp_control); else if (block__type[3]) tu_block_type_3(sc, block__type[3]); /* * The 21142/3 requires us to setup CSRs 13, 14, and 15 in * a certain way when using the MII port. */ if (sc->tu_flags.dc21142 || sc->tu_flags.dc21143) { TU_WCSR32(sc->tu_csr[13], 0x0); TU_WCSR32(sc->tu_csr[15], 0x8); TU_WCSR32(sc->tu_csr[14], 0x0); } /* * Don't muck with the MII if auto-negotiation is enabled. */ if (!sc->tu_flags.auto_enabled) tu_set_mii_phy(sc, media_selected); /* * Setup CSR6 for using the MII port. */ cmdcsr = TU_CSR6_PS; } /* * Non-MII, SIA, or SYM. */ else if (block__type[0]) cmdcsr = tu_block_type_0(sc, block__type[0], gp_control); else if (block__type[2]) cmdcsr = tu_block_type_2(sc, block__type[2]); else if (block__type[4]) cmdcsr = tu_block_type_4(sc, block__type[4]); else { printf("tu%d: no SROM info for selected media\n", unit); } return cmdcsr; } /* * tu_select_port() * This routines programs the necessary (SIA, GPR, and/or MII) registers * for a given port/media. Since the programming of CSR6 is very closely * tied to this operation, we also determine what the contents of this * register ought to be (for the given media/port) within this routine. * * Arguments * sc pointer to the softc structure * port One of the following: * TU_PORT_AUI --- 10Base5 (ThickWire) * TU_PORT_BNC --- 10Base2 (ThinWire) * TU_PORT_UTP --- 10BaseT Half-Duplex * TU_PORT_FDX --- 10BaseT Full-Duplex * TU_PORT_100 --- 100BaseTX Half-Duplex * TU_PORT_100F --- 100BaseTX Full-Duplex * TU_PORT_100FX --- 100BaseFX Half-Duplex * TU_PORT_100FXF --- 100BaseFX Full-Duplex * * Note: If the controller has an MII PHY, then 10BaseT * (Half/Full-Duplex) is programmed in MII mode (as * opposed to the 10BaseT Serial interface). * * Return Value * - Value to use in programming CSR6. */ static u_int tu_select_port(struct tu_softc *sc, int port) { u_char media_code = tu_srom_media_code[port]; u_int cmdcsr; /* * Use info in the SROM to determine what we need to do * for programming the SIA registers and what needs to * go in CSR6. */ cmdcsr = tu_use_srom_info(sc, media_code); /* * For the 21140 and 21143, there's a MustBeOne bit. */ if (sc->tu_flags.dc21140 || sc->tu_flags.dc21143) cmdcsr |= TU_CSR6_MBO; /* * Setup for Transmit Threshold modes */ if (sc->tu_flags.fast_speed) { /* * If must have Store-n-Forward, then oblige. Otherwise, * use whatever threshold we have stored in softc. */ if (sc->tu_flags.sf_mode || tu_sf_mode) cmdcsr |= TU_CSR6_SF; else cmdcsr |= sc->tu_tr << 14; lan_set_attribute(sc->ehm.current_val, NET_SPEED_NDX, (void *)NET_SPEED_100MB); } else { /* * If the 10/100 21140, 21142, or 21143 chips are used in the * 10 mode, then we must always set TTM to say so. */ cmdcsr |= TU_CSR6_TTM; lan_set_attribute(sc->ehm.current_val, NET_SPEED_NDX, (void *)NET_SPEED_10MB); } /* * If MII/SYM port, then we always set Heart-Beat Disable */ if (cmdcsr & TU_CSR6_PS) cmdcsr |= TU_CSR6_HBD; /* * Turn on full-duplex if enabled */ if (sc->tu_flags.full_duplex) { cmdcsr |= TU_CSR6_FD; lan_set_attribute(sc->ehm.current_val, NET_FDX_NDX, (void *)1); } else { lan_set_attribute(sc->ehm.current_val, NET_FDX_NDX, (void *)0); } /* * Turn on external loopback if loopback is requested. Otherwise, we * enter the normal mode of operation. */ if (sc->is_if.if_flags & IFF_LOOPBACK) { cmdcsr |= TU_OM_EXL; lan_set_attribute(sc->ehm.current_val, NET_LOOP_NDX, (void *)1); } else { cmdcsr |= TU_OM_NOR; lan_set_attribute(sc->ehm.current_val, NET_LOOP_NDX, (void *)0); } /* * Turn on the Pass all Multicast addresses flag if so requested. */ if (sc->is_if.if_flags & IFF_ALLMULTI) cmdcsr |= TU_CSR6_PM; /* * Finally, turn on the promiscuous flag if so requested. */ if (sc->is_if.if_flags & IFF_PROMISC) { cmdcsr |= TU_CSR6_PR; lan_set_attribute(sc->ehm.current_val, NET_PROMISC_NDX, (void *)1); } else { lan_set_attribute(sc->ehm.current_val, NET_PROMISC_NDX, (void *)0); } return cmdcsr; } /* * tuattach() * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. Print ethernet address for unit. * * Arguments: * ctlr pointer to controller struct for the interface. * * Return Value: * None. */ static int tuattach(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; register struct ifnet *ifp = &sc->is_if; register struct sockaddr_in *sin; /* * If this driver was linked into the kernel, set the ALV_STATIC * bit. This causes the sizer to put "config_driver tu" into * the configuration file, resulting in a kernel with the driver * linked into it. * * If this driver was dynamically loaded, set the ALV_NOSIZER * bit so that the sizer won't put the config_driver line in the * configuration file. */ if (tu_data.cfg_state == SUBSYSTEM_DYNAMICALLY_CONFIGURED) ctlr->alive |= ALV_NOSIZER; else ctlr->alive |= ALV_STATIC; ifp->if_addrlen = 6; /* media address len */ ifp->if_hdrlen = sizeof(struct ether_header) + 8 ; ifp->if_mtu = ETHERMTU; ifp->if_mediamtu = ETHERMTU; ifp->if_type = IFT_ETHER; ifp->if_timer = 0; ifp->if_sysid_type = sc->tu_sysid_type; ((struct arpcom *)ifp)->ac_flag = 0; sin = (struct sockaddr_in *)&ifp->if_addr; sin->sin_family = AF_INET; sc->is_ac.ac_bcastaddr = (u_char *)etherbroadcastaddr; sc->is_ac.ac_arphrd = ARPHRD_ETHER; /* * Setup baudrate as we know it: updated again later in tuinit() */ if (!sc->tu_flags.fast_speed) ifp->if_baudrate = ETHER_BANDWIDTH_10MB; else ifp->if_baudrate = ETHER_BANDWIDTH_100MB; ifp->if_affinity = NETALLCPU; ifp->lk_softc = &sc->lk_tu_softc; ifp->if_version = "DEC TULIP (10/100) Ethernet Interface"; simple_lock_setup(&sc->lk_tu_softc, tu_softc_lockinfo); printf("tu%d: %s, hardware address: %s\n", unit, ifp->if_version, ether_sprintf(sc->is_addr)); printf("tu%d: %s\n", unit, sc->tu_port_selection); /* * Now initialize the function pointers and interface flags. * The order here is important, in case this is a re-attach * and someone still has a reference to this old ifnet * structure. */ ifp->if_ioctl = tuioctl; ifp->if_watchdog = tuwatch; ifp->if_start = tu_start; mb(); ifp->if_output = ether_output; mb(); ifp->if_flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOTRAILERS|IFF_SIMPLEX; /* * Add the interface to the list of configured ifnet structures, * and attach it to the packet filter. */ attachpfilter(sc->is_ed); if_attach(ifp); /* All set to take interrupts now. */ handler_enable(sc->hid); tu_configured ++; /* # of tu devices successfully cfg */ /* * Set the known non-zero network attributes for the hardware * management facility and register the adapter. */ lan_set_common_attributes(sc->ehm.current_val, sc->is_ed); lan_register_adapter(&sc->ehm, ctlr); return(0); } /* * Name: tuunattach(bus, ctlr); * * Arguments: * bus: A pointer to the bus structure for the bus that * this device is on. * ctlr: A pointer to the controller structure for this * instance of the device. * * Functional Description: * * Called per controller to stop device and free memory * and other resources in preparation for unloading the * driver or for powering off the bus to which this device * is attached. This function should undo everything that * was done in the probe/attach routines. * * Return Codes: * ESUCCESS The device was successfully stopped * and all associated memory and DMA * resources freed. * EBUSY The device is still in use * ENOTSUP Unattach/unload not supported for * this device */ static int tu_unattach(struct bus *bus, struct controller *ctlr) { int unit = ctlr->ctlr_num; int s; int status; struct tu_softc *sc = tu_softc[unit]; struct ifnet *ifp = &sc->is_if; /* * Make sure the interface is down. Any other * errors returned by if_detach shouldn't stop * us from shutting down the interface. */ status = if_detach(ifp); if (status == EBUSY) return EBUSY; else if (status == ESUCCESS) { detachpfilter(sc->is_ed); } /* * Terminate all threads */ thread_terminate (sc->intr_work_thread); /* * Grab the softc lock and shutdown the device. */ s = splimp(); simple_lock(&sc->lk_tu_softc); tu_shutdown(ctlr); simple_unlock(&sc->lk_tu_softc); splx(s); /* * Disable our interrupt handler from servicing * any further interrupts for this device. */ handler_disable(sc->hid); /* * Unregister the shutdown routine */ drvr_register_shutdown(tu_shutdown, (void*)ctlr, DRVR_UNREGISTER); /* * Free up the softc lock */ simple_lock_terminate(&sc->lk_tu_softc); /* * Free up data structures and resources used by adapter */ sc->is_ed = NULL; tu_freemem(unit); tu_configured --; return ESUCCESS; } /* * tuinit() * Initialize interface. Make sure things are cleaned-up from past * instances of the driver and ready descriptors in both rings for * a hand-off to Tulip. * * Turn on the appropriate control bits in the Tulip's command register * based on what is requested of the interface. If promiscuous mode is * not selected, then execute the setup frame for address filtering. * * The following registers are setup in this routine: * * CSR3 - Receive ring address * CSR4 - Transmit ring address * CSR5 - Status register (cleared) * CSR6 - Command register * CSR7 - Interrupt mask register * * If the initialization succeeds, the interface will be marked as being * in the running state, and Tulip will have started working on the rings. * * Arguments: * unit The unit number of the interface * * Return Value: * - EIO * - ENOMEM * - ENOBUFS * - ESUCCESS */ static int tuinit(int unit) { register struct controller *ctlr = tuinfo[unit]; register struct tu_softc *sc = tu_softc[unit]; register struct ifnet *ifp = &sc->is_if; register int i, j, k, port, cmdcsr, status; struct mbuf *pending_xmit[TU_NTRING]; /* * Ensure baudrate is up-to-speed */ if (!sc->tu_flags.fast_speed) ifp->if_baudrate = ETHER_BANDWIDTH_10MB; else ifp->if_baudrate = ETHER_BANDWIDTH_100MB; /* * If we're re-initializing, then there's some chance that we * have one or more transmits pending in the ring. If so, put * them back on the if-queue for re-transmission. We'll start * output later at the end of this routine if needed. */ for (i=0,j=sc->otindex,k=0; ismbuf[j]) { pending_xmit[k++] = sc->smbuf[j]; sc->smbuf[j] = NULL; } INIT_TDESC(&sc->tring[j]); } /* * Prepend starting with the most recent transmit. This way, the * oldest packet will be at the head of the interface queue. */ IFQ_LOCK(&ifp->if_snd); for (i=k-1; i>=0; i--) { IF_PREPEND_NOLOCK(&ifp->if_snd, pending_xmit[i]); } IFQ_UNLOCK(&ifp->if_snd); /* * Init the receive ring descriptors */ for (i=0; irring[i]); } /* * (Re)initialize all indicies into the transmit and receive rings. */ sc->rindex = sc->itindex = sc->otindex = sc->ntinuse = sc->ntpendg = 0; /* * Before we setup any CSRs, we need to find out if a chip reset is * called for first. */ tu_special_chip_reset(ctlr); /* * Tell the chip where the rings are. */ TU_WCSR32(sc->tu_csr[3], (u_int)sc->rring_phys); TU_WCSR32(sc->tu_csr[4], (u_int)sc->tring_phys); /* * Setup these CSRs to their initial values. */ TU_WCSR32(sc->tu_csr[5], TU_CSR5_INIT); /* Clear Status Bits */ TU_WCSR32(sc->tu_csr[7], TU_CSR7_INIT); /* Interrupt Mask Bits */ /* * Program the chip for whatever port/media we've selected. The * routine below will also figure out what the Command register * should have. */ cmdcsr = tu_select_port(sc, tu_flags_to_port(sc)); /* * If not promiscuous, then we need to issue a setup frame * for specific address filtering. */ if (!(ifp->if_flags & IFF_PROMISC)) { TU_WCSR32(sc->tu_csr[6], cmdcsr | TU_CSR6_ST); /* * The following delay is really only necessary for the * DE500-XA in 100 mbit mode (for reasons unknown). All * other implementations of the 2114x seem to work fine * without this delay. * * The problem exhibited in the DE500-XA was not a severe * problem, but it resulted in the first real transmit after * this operation being discarded. The Tulip told us that * the transmit made it without error, but it really didn't. */ DELAY(2*1000); if ((status = tu_setup_frame(ctlr)) != ESUCCESS) { printf("tu%d: initialization failed\n", unit); return status; } } /* * If auto-negotiation is not enabled, then start the transmit * and receive processes. */ if (!sc->tu_flags.auto_enabled) { TU_WCSR32(sc->tu_csr[6], cmdcsr | TU_CSR6_ST | TU_CSR6_SR); /* * Make sure the auto negotiation timer isn't ticking. */ if (sc->tu_flags.timer_on) { untimeout((void(*)(void *))tu_auto_negotiate, sc); sc->tu_flags.timer_on = 0; } } else { /* * Make arrangements to kick off the auto negotiation state * machine. */ sc->tu_auto_ticks = 0; sc->tu_auto_state = TU_AUTO_STATE_INITIAL; /* Ensure timer is ticking */ if (!sc->tu_flags.timer_on) { timeout((void(*)(void *))tu_auto_negotiate, sc, 512); sc->tu_flags.timer_on = 1; } } /* * We are active and running now. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* * And set time counters zeroed. */ sc->ztime = time.tv_sec; /* * Before we return, start the output process to ensure that * requeued packets don't sit in the ifnet queue forever. */ if (ifp->if_snd.ifq_head) tustart(ifp); return ESUCCESS; } /* * tustart(ifp) * Interface start routine - start output on the interface * * Arguments: * ifp The ifnet pointer, pointing to the intf. to o/p on. * * Return Value: * None. */ static int tustart(struct ifnet *ifp) { register int unit = ifp->if_unit; register struct tu_softc *sc = tu_softc[unit]; register int ntpendg = sc->ntpendg; /* * Tell the world we're busy processing the interface send-queue. */ ifp->if_flags |= IFF_OACTIVE; /* * Remove packets from the interface queue and put them into the * transmit ring while we can. */ while (1) { struct mbuf *m; IF_DEQUEUE(&ifp->if_snd, m); if (m) /* * Have an mbuf chain to give to the device */ if (tu_put_transmit(sc, m)) /* * Was put on the ring successfully, or was * dropped; in either case, we want to continue. */ continue; else { /* * Packet was put back on the interface queue. * Keep OACTIVE set since there's no point in * coming here again until after we have room * in the transmit ring. */ break; } else { /* * Interface queue has been emptied. Check the queue * head again, however, before we quit. This check is * necessary to account for the race where the caller * of if_start finds active set (and therefore does not * call start) *after* we've decided that the queue is * empty but before we get here to clear active. */ ifp->if_flags &= ~IFF_OACTIVE; mb(); if (ifp->if_snd.ifq_head) { ifp->if_flags |= IFF_OACTIVE; continue; } else /* done */ break; } } /* * If we put something in the ring, then have to tell TULIP about it. * Also need to flag the interface as being active. Reset the timer * for this new entry. */ if (ntpendg != sc->ntpendg) { TU_WCSR32(sc->tu_csr[1], 1); ifp->if_timer = TU_WATCH_FREQUENCY; } return(0); } /* * tu_put_transmit * Process an mbuf chain representing an ethernet packet (from our point * of view) into the transmit ring. * * Arguments: * sc The controller structure for the interface. * m The mbuf chain to be copied to the RAM buffer. * * Return Value: * 1 If the packet was successfully placed in the ring or if it was * dropped (transmission should continue) * 0 If the packet was put back on the interface queue (transmission * should not continue in this case) */ static int tu_put_transmit(struct tu_softc *sc, struct mbuf *m) { register struct ifnet *ifp = &sc->is_if; register int unit = ifp->if_unit; register struct controller *ctlr = tuinfo[unit]; register int i,ntfree,first,index,pktlen=0; register int j,k,nmbufs=0,cpdata=0,map_error=0; register struct mbuf *n,*mp,*m0 = m; register TUDESC *tp; /* * Walk through the mbuf chain and count the number of descriptors we * are going to need for this frame. Drop 0-length mbufs while we are * at it. */ while (m) { if (m->m_len) { /* * Update pktlen and # of mbufs, remember current mbuf, * and move on to the next one. */ pktlen += m->m_len; mp = m; m = m->m_next; nmbufs++; } else { n = m->m_next; m->m_next = 0; m_freem(m); /* * If the dropped mbuf was not at the head, then link * the previous entry to the one after the dropped one; * otherwise, simply update the head mbuf ptr (m0). */ if (m != m0) mp->m_next = n; else m0 = n; m = n; } } /* * If there's nothing to transmit, then quit now. */ if (pktlen == 0) return 1; /* * Make sure the first mbuf has a packet header */ if (!(m0->m_flags & M_PKTHDR)) { m_freem(m); return 1; } /* * Check to see if we have enough empty slots in the transmit ring * to scatter the mbuf chain. Note: filling up the ring 100% is not * allowed; there must always be at least one unused entry. */ ntfree = TU_NTRING - sc->ntinuse; if (ntfree <= nmbufs) { /* * We don't. If the mbuf chain doesn't exceed our ring limit, * or if it does but we don't have a single slot to spare, then * stick the packet back on the interface queue. We'll get to * it when TULIP catches up with us. Otherwise, we do the * transmission now by copying the data into a large mbuf. */ if ((nmbufs < TU_NTRING) || (ntfree == 1)) { IF_PREPEND(&ifp->if_snd, m0); return 0; } else cpdata++; } /* * If we decided above to copy (for whatever reason), then try * to do it now. */ if (cpdata) { /* * If the packet can be accomodated in a small mbuf (without * a pkthdr), then try to get that kind of mbuf. Otherwise, * go for a 2k mbuf. */ if (pktlen <= MLEN) { MGET(m, M_DONTWAIT, MT_DATA); } else TUMCLGET(m, M_DONTWAIT); if (m) { u_int offset = 0; /* Got mbuf; do the ugly copying :( */ for (n=m0; n; offset+=n->m_len,n=n->m_next) { bcopy(mtod(n, void *), (void *)(mtod(m, vm_offset_t) + offset), (u_int) n->m_len); } /* * Update the new mbuf length, and free the old chain. */ m->m_len = offset; m_freem(m0); m0 = m; } else { /* * Can't get a buffer to do the copy. Drop the packet. */ m_freem(m0); return 1; } } /* * We should be all set at this point. Go through the mbuf chain and * setup a xmit descriptor for each mbuf. If the mbuf crosses a page * boundary (doesn't really happen right now), then use the second * buffer entry within the descriptor. */ for (i=0,m=m0,index=sc->itindex; m; i++,m=m->m_next) { register long rbc, bc = m->m_len; register vm_offset_t va = mtod(m, vm_offset_t); register int flags = DMA_OUT | DMA_ALL; register sglist_t sglp; tp = &sc->tring[index]; /* * If this is the first time through, then save the index (used * later), flag the desc as being the first, and save the mbuf * pointer so we can free it later in the interrupt routine. */ if (i == 0) { tp->tu_control |= TU_TFS; sc->smbuf[index] = m; first = index; } else tp->tu_own = TU_TULIP; /* * Call the DMA mapping routine now to translate the mbuf va to * a (set of) physical bus-address(es). As was said earlier, if * the mbuf crosses a page-boundary, then we'll get back two * entries, otherwise we need to deal with just one. */ rbc = dma_map_load(bc, va, (struct proc*)0, ctlr, &sc->tsglp[index], bc, flags); sglp = sc->tsglp[index]; if (rbc != bc) { map_error++; break; } /* * Setup the 1st address and byte-count pair. */ tp->tu_bfaddr1 = (u_int)sglp->sgp[0].ba; tp->tu_bfsize1 = sglp->sgp[0].bc; /* * And if we had a page-crossing, then do the next one. */ if (sglp->val_ents > 1) { tp->tu_bfaddr2 = (u_int)sglp->sgp[1].ba; tp->tu_bfsize2 = sglp->sgp[1].bc; } NEXT_TINDEX(index); } /* * If we had trouble mapping virtual adresses to bus-addresses, then * undo what we did above and drop the packet. We don't really expect * this to happen, so alert the sys manager. */ if (map_error) { printf("tu%d: dma mapping error on xmt; packet dropped\n",unit); for (j=0,k=sc->itindex; j<=i; j++,NEXT_TINDEX(k)) { INIT_TDESC(&sc->tring[k]); sc->smbuf[k] = NULL; } m_freem(m0); return 1; } /* * Flag the last descriptor/segment, request interrupt on completion, * and the packet is ready to go. */ tp->tu_control |= TU_TLS | TU_IC; mb(); sc->tring[first].tu_own = TU_TULIP; /* * A couple of important book-keeping tasks before we return. */ sc->itindex = index; sc->ntinuse += i; sc->ntpendg += 1; return 1; } /* * tuwatch() * Watchdog timer routine - goes off whenever a transmit times-out. * * Arguments: * unit The unit number of the interface * * Return Value: * None. */ static int tuwatch(int unit) { register struct tu_softc *sc = tu_softc[unit]; register struct ifnet *ifp = &sc->is_if; register int s, index; s = splimp(); simple_lock(&sc->lk_tu_softc); index = sc->otindex; /* * Before we reset the interface, remove the offending transmit * from the ring. We always remove the first packet. */ if ((sc->ntpendg) && (sc->smbuf[index])) { struct mbuf *m = sc->smbuf[index]; /* * Count this packet in error. */ ifp->if_opackets++; tu_transmit_errors(sc, 0); /* * Free the buffer (chain), and dereference it. */ m_freem(m); sc->smbuf[index] = NULL; } ifp->if_timer = 0; if (tu_error_printfs) printf("tu%d: interface reset: transmit timeout\n", unit); tureset(unit); simple_unlock(&sc->lk_tu_softc); splx(s); return(0); } /* * tureset() * Reset the interface. * * Argument * unit The unit number of the interface * * Return Value * none */ static void tureset(int unit) { register struct controller *ctlr = tuinfo[unit]; register struct tu_softc *sc = tu_softc[unit]; register struct ifnet *ifp = &sc->is_if; /* * Stop the network activity and start things over again. */ ifp->if_flags &= ~IFF_RUNNING; tu_chip_reset(ctlr); tuinit(unit); } /* tuioctl() * Process an ioctl request. In doing so, the interface is * restarted and reinitialized (in most cases). * * Arguments: * ifp Pointer to the ifnet structure * cmd The ioctl to be performed * data The corresponding data for the ioctl. * * Return Value: * - EIO * - EINVAL * - ENOMEM * - ENOBUFS * - ESUCCESS */ static int tuioctl(struct ifnet *ifp, u_int cmd, caddr_t data) { register struct tu_softc *sc = tu_softc[ifp->if_unit]; register struct controller *ctlr = tuinfo[ifp->if_unit]; register unit = ifp->if_unit; struct ifreq *ifr = (struct ifreq *)data; struct ifdevea *ifd = (struct ifdevea *)data; struct ifchar *ifc = (struct ifchar *)data; struct ctrreq *ctr = (struct ctrreq *)data; int s, i, j, lock_on = 1, status = ESUCCESS; u_short ifmtu, speed; u_char mclist_buf[NET_SZ_MCLIST]; int retval; s = splimp(); simple_lock(&sc->lk_tu_softc); switch (cmd) { case SIOCENABLBACK: ifp->if_flags |= IFF_LOOPBACK; if (ifp->if_flags & IFF_RUNNING) tureset(unit); break; case SIOCDISABLBACK: ifp->if_flags &= ~IFF_LOOPBACK; if (ifp->if_flags & IFF_RUNNING) tureset(unit); break; case SIOCRPHYSADDR: /* * Return the current and default physical addresses. */ bcopy(sc->is_addr, ifd->current_pa, 6); bcopy(sc->tu_rom_addr, ifd->default_pa, 6); break; case SIOCSPHYSADDR: /* * Set (over-ride) the hardware physical address */ bcopy(ifr->ifr_addr.sa_data, sc->is_addr, 6); pfilt_newaddress(sc->is_ed->ess_enetunit, sc->is_addr); /* * Since the physical address has changed, issue a new * setup filter to the controller. */ if (ifp->if_flags & IFF_RUNNING) status = tu_setup_frame(ctlr); /* * If an IP address has been configured then an ARP * packet must be broadcast to tell all hosts which * currently have our address in their ARP tables to * update their information. */ simple_unlock(&sc->lk_tu_softc); splx(s); if (((struct arpcom *)ifp)->ac_flag & AC_IPUP) { rearpwhohas((struct arpcom *)ifp); } /* Also call in to update the af_link address. */ if_sphyaddr(ifp, ifr); lan_set_attribute(sc->ehm.current_val, NET_MAC_NDX, ether_sprintf(sc->is_addr)); lock_on = 0; break; case SIOCADDMULTI: /* * Add the indicated multicast address (or bump the * in-use counter if it is already registered). If * this is a new address, notify the adapter of the * change. */ retval = lan_add_multi(&sc->is_multi, (u_char *)ifr->ifr_addr.sa_data); if (retval == LAN_MULTI_FAILED) { printf("tu%d: multicast limit\n", unit); status = EIO; } else if (retval == LAN_MULTI_CHANGED) { if (ifp->if_flags & IFF_RUNNING) status = tu_setup_frame(ctlr); /* * The list has changed. Update the attributes * for enhanced hardware management support. */ lan_build_mclist(mclist_buf, NET_SZ_MCLIST, &sc->is_multi); lan_set_attribute(sc->ehm.current_val, NET_MCLIST_NDX, mclist_buf); } break; case SIOCDELMULTI: /* * Delete the indicated multicast address. If no * other users exist for this address, restart * the adapter with the new list. */ retval = lan_del_multi(&sc->is_multi, (u_char *)ifr->ifr_addr.sa_data); if (retval == LAN_MULTI_CHANGED) { if (ifp->if_flags & IFF_RUNNING) status = tu_setup_frame(ctlr); /* * The list has changed. Update the attributes * for enhanced hardware management support. */ lan_build_mclist(mclist_buf, NET_SZ_MCLIST, &sc->is_multi); lan_set_attribute(sc->ehm.current_val, NET_MCLIST_NDX, mclist_buf); } break; case SIOCRDCTRS: case SIOCRDZCTRS: ctr->ctr_ether = sc->ctrblk; ctr->ctr_type = CTR_ETHER; ctr->ctr_ether.est_seconds = (time.tv_sec - sc->ztime) > 0xfffe ? 0xffff : (time.tv_sec - sc->ztime); if (cmd == SIOCRDZCTRS) { sc->ztime = time.tv_sec; bzero(&sc->ctrblk, sizeof(struct estat)); } break; case SIOCSIFADDR: ifp->if_flags |= IFF_UP; tureset(unit); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_RUNNING) tureset(unit); break; case SIOCSIPMTU: bcopy(ifr->ifr_data, (u_char *)&ifmtu, sizeof(u_short)); if (ifmtu > ETHERMTU || ifmtu < IP_MINMTU) status = EINVAL; else { ifp->if_mtu = ifmtu; lan_set_attribute(sc->ehm.current_val, NET_MTU_NDX, (void *)ifmtu); } break; case SIOCIFSETCHAR: status = tu_set_ifchar(unit, ifc->ifc_auto_sense, ifc->ifc_media_type, ifc->ifc_media_speed, ifc->ifc_full_duplex_mode); break; case SIOCSMACSPEED: bcopy(ifr->ifr_data, (u_char *)&speed, sizeof(u_short)); status = tu_set_ifchar(unit, (int)-1, (int)-1, (int)speed, (int)-1); break; default: status = EINVAL; } /* * If we've already unlocked and restored the spl, don't do so again. */ if (lock_on) { simple_unlock(&sc->lk_tu_softc); splx(s); } return (status); } /* tu_set_ifchar() * Executes change/setup of media type, speed, half/full-duplex and * enable/disabling of auto-negotiation as appropriate for the device. * * Arguments: * unit : unit number * auto : 0 (disable) 1 (enable) auto negotiation * media : media type: as defined in lan_common.h * speed : 10, 20, 100, or 200 * duplex : 0 (half) 1 (full) duplex * * -1 on any or all to indicate no change in that configuration. * * Return Value: * - EINVAL * - ESUCCESS */ static int tu_set_ifchar(int unit, int auto_sense, int media, int speed, int duplex) { struct tu_softc *sc = tu_softc[unit]; struct tuflags orig_flags = sc->tu_flags; int status = ESUCCESS; /* * Auto sense is really auto negotiation for us */ if (auto_sense > -1) { sc->tu_flags.auto_enabled = auto_sense == AUTO_ENB; } /* * Get media type, if provided */ if (media > -1) { sc->tu_flags.aui_mode = 0; sc->tu_flags.bnc_mode = 0; sc->tu_flags.fx_mode = 0; switch (media) { case LAN_MEDIA_UTP: break; case LAN_MEDIA_AUI: sc->tu_flags.aui_mode = 1; break; case LAN_MEDIA_BNC: sc->tu_flags.bnc_mode = 1; break; case LAN_MEDIA_FIBER: sc->tu_flags.fx_mode = 1; break; default: status = EINVAL; } } /* * Get speed, if provided */ if (speed > -1) { sc->tu_flags.fast_speed = 0; sc->tu_flags.full_duplex = 0; switch (speed) { case 20: sc->tu_flags.full_duplex = 1; case 10: break; case 200: sc->tu_flags.full_duplex = 1; case 100: sc->tu_flags.fast_speed = 1; break; default: status = EINVAL; } } /* * Half or full-duplex */ if (duplex > -1) { sc->tu_flags.full_duplex = duplex == FDX_ENB; } /* * Sanity checks: mostly for typos and the uninformed user */ if (sc->tu_flags.auto_enabled && !sc->tu_flags.auto_capable) { printf("tu%d: device incapable of auto negotiation\n", unit); status = EINVAL; } if (sc->tu_flags.aui_mode || sc->tu_flags.bnc_mode) { if (sc->tu_flags.full_duplex) { printf("tu%d: can't do full-duplex on AUI-BNC\n", unit); status = EINVAL; } if (sc->tu_flags.auto_enabled) { printf("tu%d: can't auto negotiate on AUI-BNC\n",unit); status = EINVAL; } } /* * Quit now if it ain't cool: don't forget to restore original flags! */ if (status != ESUCCESS) { sc->tu_flags = orig_flags; return status; } /* * If we're dealing with an auto-negotiation capable device, then have * to find out if auto negotiation is being changed. */ if (sc->tu_flags.auto_capable) { int more = 1; /* * Update our ability since the config may have changed */ tu_flags_to_ability(sc); if (sc->tu_flags.auto_enabled) { printf("tu%d: auto negotiation %s: will advertise",unit, orig_flags.auto_enabled ? "restart" : "enabled"); } else if (orig_flags.auto_enabled) { printf("tu%d: auto negotiation disabled: selecting", unit); } else more = 0; if (more) { printf(" %s: %s duplex\n", sc->tu_flags.fast_speed ? "100BaseTX":"10BaseT", sc->tu_flags.full_duplex ? "full" : "half"); } } /* * Reset the interface in the event something was actually changed. */ tureset(unit); return ESUCCESS; } /* * tuintr() * Interface interrupt routine. * * Arguments: * unit The unit number of the interface * * Return Value: * INTR_SERVICED The interrupt was for us * INTR_NOT_SERVICED The interrupt was not for us */ static int tuintr(int unit) { register u_int s; register struct tu_softc *sc = tu_softc[unit]; int status = INTR_NOT_SERVICED; s = splimp(); /* * If this interrupt is for us, disable further interrupts * from the device and wake up the interrupt worker thread. */ if (TU_RCSR32(sc->tu_csr[5]) & (TU_CSR5_NIS|TU_CSR5_AIS)) { TU_WCSR32(sc->tu_csr[7], 0); thread_wakeup((vm_offset_t)&sc->intr_work_flag); status = INTR_SERVICED; } splx(s); return status; } /* * tu_intr_work_thread () * Handles interrupt processing at task level * * Arguments: * sc Pointer to the softc struct * * Return Value: * None. */ static void tu_intr_work_thread(struct tu_softc *sc) { register u_int s, status; register struct ifnet *ifp = &sc->is_if; assert_wait ((vm_offset_t)&sc->intr_work_flag, TRUE); while (TRUE) { /* * Block until awakened by tuintr. */ thread_block(); if (thread_should_halt(sc->intr_work_thread)) { thread_halt_self(); /* No return */ } s = splimp(); simple_lock(&sc->lk_tu_softc); /* * Tell the world we're busy processing transmit and * receive completions. */ ifp->if_flags |= IFF_OACTIVE; /* * Read the interrupt status and clear it. Then check * for any special reasons for the interrupt. */ status = TU_RCSR32(sc->tu_csr[5]); TU_WCSR32(sc->tu_csr[5], status); if (status & TU_CSR5_SE) tu_system_error(sc, status); if (status & TU_CSR5_RU) sc->is_ed->ess_missed++; /* * Process the receive and transmit rings while they have * something in them to work on. */ tu_receive_int(sc); tu_transmit_int(sc); /* * If there's room in the transmit ring, and we have * something pending in the interface send-queue, then * process it. * NOTE: The ordering below is important: refer to * similar code & comments in the start routine. */ ifp->if_flags &= ~IFF_OACTIVE; mb(); if ((sc->ntinuse < TU_NTRING-1) && (ifp->if_snd.ifq_head)) tustart(ifp); /* * Tickle the transmit and receive processes. */ TU_WCSR32(sc->tu_csr[1], 1); TU_WCSR32(sc->tu_csr[2], 1); /* * Re-enable interrupts and get ready to block again */ assert_wait ((vm_offset_t)&sc->intr_work_flag, TRUE); TU_WCSR32(sc->tu_csr[7], TU_CSR7_INIT); simple_unlock(&sc->lk_tu_softc); splx(s); } } /* * tu_system_error() * Process a "system error" as defined by Tulip. * * Arguments: * sc Pointer to the softc structure for this unit * status Contents of CSR5 (status register) at the time * * Return Value: * None. */ static void tu_system_error(struct tu_softc *sc, u_int status) { register struct ifnet *ifp = &sc->is_if; register int unit = ifp->if_unit; /* * We get a "System Error" for the following reasons today: * - Parity Error * - Master Abort * - Target Abort * * In the first case, the only recovery mechanism is a chip * reset. In the latter two, we can continue from the error * by simply clearing the System-Error bit in the status * register --- something we already do on each interrupt. * And in all threee cases, we should clear out the errors * from the PCI Config Status area also. */ register u_int cfcs_value = TU_RCSR32(sc->tu_cfcs); /* * We'll be writing the original bits in the command portion, and * the error bits (W1C) in the status word. */ TU_WCSR32(sc->tu_cfcs, cfcs_value); switch (status & TU_CSR5_EB) { case TU_EB_PARITY: printf("tu%d: system error: parity error\n",unit); tureset(unit); break; case TU_EB_MASTER: printf("tu%d: system error: master abort\n",unit); break; case TU_EB_TARGET: printf("tu%d: system error: target abort\n",unit); break; default: printf("tu%d: system error: reason unknown\n",unit); } } /* * tu_transmit_int() * Process a transmit interrupt completion. * * Arguments: * sc Pointer to the softc structure for this unit * * Return Value: * None. */ static void tu_transmit_int(struct tu_softc *sc) { register struct ifnet *ifp = &sc->is_if; register int unit = ifp->if_unit; register int index = sc->otindex; register TUDESC *tp = &sc->tring[index]; register int i,j,status,first,ndesc; register int ring_error=0,xmt_done; register short pktlen; /* * Scan the ring looking for completed packets; quit when we encounter * one that is owned by Tulip. */ while ((sc->ntpendg) && (tp->tu_own == TU_HOST)) { /* * Have packet to look at; make sure ALL the descriptors * have been closed before saying that the transmit is done. * Add-up the packet length while we're at it. We also need * to add the # of descriptors in this packet. (Used later.) */ for (xmt_done=pktlen=0,ndesc=1; ndesctu_bfsize1 + tp->tu_bfsize2; if (ndesc == 1) { /* * This is the first one; confirm that fact * and save its index. If the last-bit is set * here as well, then there was only one desc * for this packet. We're done in that case. */ if (tp->tu_control & TU_TFS) { first = index; if (tp->tu_control & TU_TLS) { xmt_done = 1; break; } } else { /* * This will keep us sane. */ ring_error = 1; break; } } else { /* * If we've hit the last one, then we're done. * Continue to make sanity checks otherwise. */ if (tp->tu_control & TU_TLS) { xmt_done = 1; break; } else if (tp->tu_control & TU_TFS) { ring_error = 1; break; } } /* * Move on to the next descriptor; quit if TULIP * is still working on it. */ NEXT_TINDEX(index); tp = &sc->tring[index]; if (tp->tu_own == TU_TULIP) return; } /* * If we're out of sync with Tulip (or we're screwed up * on our own account), then start things over again. */ if (ring_error) { printf("tu%d: transmit ring inconsistent\n",unit); tureset(unit); return; } else if (!xmt_done) /* * We'll get here if Tulip is partially done with a * packet. (Not all descriptors have been closed.) */ return; /* * Have a completed packet. Get the status out for some * convenient checking. */ status = tp->tu_status; if (!(tp->tu_control & TU_SET)) { struct mbuf *m; int ccount; m = sc->smbuf[first]; sc->smbuf[first] = NULL; /* * If the packet failed to go out due to exessive * collisions, requeue it for transmission later. Do * this, however, ONLY if the sending protocol can * deal with occasional out-of-order and/or duplicate * packets, and we haven't already requeued it. */ if ((status & TU_EC) && (m->m_flags & M_REQUE_OK) && !(m->m_flags & M_REQUED_ONCE)) { IF_PREPEND(&ifp->if_snd, m); m->m_flags |= M_REQUED_ONCE; sc->ec_reque_count++; } else { /* * Network statistics */ ifp->if_opackets++; ccount = (status & TU_CC) >> 3; sc->actual_ccount += ccount; if (ccount) { /* collision happened */ if(ccount == 1) { ifp->if_collisions += 1; INCR(sc->ctrblk.est_single,0xffffffff); } else { /* more than one collision */ ifp->if_collisions += 2; INCR(sc->ctrblk.est_multiple,0xffffffff); } } if (status & TU_DE) INCR(sc->ctrblk.est_deferred,0xffffffff); if ((status & TU_HF) && !(status & TU_UF)) /* Heartbeat Fail */ INCR(sc->ctrblk.est_collis, 0xffff); /* * Check for transmission errors. This assumes that * transmission errors are always reported in the last * descriptor of a packet. * * Item #6 from Rev 4 of the 21143-XD Eratta. The * error summary bit is sometimes not accurate for * the 21143 after Link restoration conditions. */ if (sc->tu_flags.dc21143) { /* * Sometimes, on the 21143-xD, the TU_ES (error * summary) bit will be set when there is no * error condition that actually occured. If * there are no errors set in the status, then * just clear the status. Otherwise process * a legitimate error. * * This really seems to happen mostly in * 10 half duplex. */ if ((status & TU_ES) && !(status & (TU_DE|TU_UF|TU_LF|TU_CC|TU_HF| TU_EC|TU_LC|TU_NC|TU_LO|TU_TO))) status = 0; } if (status & TU_ES) { /* transmission errors */ tu_transmit_errors(sc, status); } else { /* * Bump interface statistics */ struct ether_header *eh; if (sc->ctrblk.est_bytesent + pktlen > sc->ctrblk.est_bytesent) sc->ctrblk.est_bytesent += pktlen; INCR(sc->ctrblk.est_bloksent,0xffffffff); eh = mtod(m, struct ether_header *); if (eh->ether_dhost[0] & 1) { if (sc->ctrblk.est_mbytesent + pktlen > sc->ctrblk.est_mbytesent) sc->ctrblk.est_mbytesent += pktlen; INCR(sc->ctrblk.est_mbloksent, 0xffffffff); } /*sc->tu_flags.rx_or_tx = 1;*/ } /* * If we received an underflow error and are operating * in the 21140/fast mode with auto threshold selection * turned on, and we haven't already reached the upper * limit, then step up to the next level. */ if ((status & TU_UF) && (sc->tu_flags.fast_speed) && (sc->tu_flags.auto_tr) && (!sc->tu_flags.sf_mode)) { printf("tu%d: transmit FIFO underflow: ", unit); if (++sc->tu_tr == 4) { printf("using store-forward:\n"); sc->tu_flags.sf_mode = 1; } else { printf("threshold raised to: %d bytes\n" ,(1 << (sc->tu_tr + 7))); } sc->smbuf[first] = m; tureset(unit); return; } /* * Done with this transmit -- free the saved mbuf */ m_freem(m); } } else { /* setup frame - no action needed here */ } /* * Re-cycle the completed descriptors and update our count of * descriptors in use accordingly. Unload the dma-mapped buffer * as well. */ for (i=0,j=first; itsglp[j]); INIT_TDESC(&sc->tring[j]); sc->ntinuse--; } /* * Move on to the next descriptor. */ NEXT_TINDEX(index); sc->otindex = index; tp = &sc->tring[index]; /* * One less packet pending. If we're down to zero, turn off * the timer. */ if (--sc->ntpendg == 0) { ifp->if_timer = 0; } } } /* * tu_transmit_errors() * Parse errors occuring on transmit interrupt. Print them to the * console if debug is turned on. * * Arguments: * sc Pointer to the softc structure for this unit * status Status word from TDESC0 * * Return Value: * none */ static void tu_transmit_errors(struct tu_softc *sc, int status) { register int unit = sc->is_if.if_unit; if (status & TU_UF) { if (tu_error_printfs) printf("tu%d: transmit error: underflow error\n",unit); } if (status & TU_LF) { if (tu_error_printfs) printf("tu%d: transmit error: link failure\n",unit); } if (status & TU_EC) { if (tu_error_printfs) printf("tu%d: transmit error: excessive collisions\n",unit); sc->ctrblk.est_sendfail_bm |= 1; } if (status & TU_LC) { if (tu_error_printfs) printf("tu%d: transmit error: late collision\n",unit); } if (status & TU_NC) { if (tu_error_printfs) printf("tu%d: transmit error: no carrier\n",unit); sc->ctrblk.est_sendfail_bm |= 2; } if (status & TU_LO) { if (tu_error_printfs) printf("tu%d: transmit error: lost carrier\n",unit); sc->ctrblk.est_sendfail_bm |= 2; } if (status & TU_TO) { if (tu_error_printfs) printf("tu%d: transmit error: jabber time-out\n",unit); } sc->is_if.if_oerrors++; if (sc->ctrblk.est_sendfail == 0xffff) return; sc->ctrblk.est_sendfail++; } /* * tu_receive_int() * Process a receive interrupt completion. * * Arguments: * sc Pointer to the softc structure for this unit * * Return Value: * None. */ static void tu_receive_int(struct tu_softc *sc) { register TUDESC *rp = &sc->rring[sc->rindex]; register int index; /* * Traverse the receive ring looking for packets to pass back. * The search is complete when we find a descriptor not in use * (which means owned by TULIP). Note that unlike the transmit * path, we only have one descriptor per packet here. */ for (index=sc->rindex; rp->tu_own==TU_HOST; rp=&sc->rring[index]) { /* * Have a completed packet; check the Error-Summary bit. */ if(!(rp->tu_status & TU_ES)) { register struct mbuf *n, *m = sc->rmbuf[index]; struct ether_header eh; register int len; if (TU_RCSR32(sc->tu_csr[8]) & 0x1FFE0000) { tu_receive_overflow(sc); break; } /* * Make sure we want this packet (really only an issue * for group addresses) */ if (tu_frame_check(mtod(m, u_char *), sc)) { /* * Load the ethernet header for the ether_input call * below. Also re-order the two bytes of ether_type * within the header before we forget (ether_input * depends on this). */ eh = *(mtod(m, struct ether_header *)); eh.ether_type = ntohs((u_short)eh.ether_type); /* * Drop the header and CRC bytes */ len = rp->tu_rflen - sizeof(struct ether_header) - 4; /* * If the packet can be accommodated in a small mbuf * (with pkthdr), then get one of those. Otherwise, get * a large 2k buffer. */ if (len <= MHLEN) { MGETHDR(n, M_DONTWAIT, MT_DATA); } else TUMCLGET(n, M_DONTWAIT); /* * Do the ugly copying and setup the pkthdr fields if * there was no problem in getting the mbuf. Drop the * packet, otherwise. */ if (n) { bcopy((void *)(mtod(m, vm_offset_t) + sizeof(struct ether_header)), mtod(n, void *), len); /* * Receive buffer is free: hand it over for * a new receive. No need to delay this until * after ether_input is called. */ INIT_RDESC(rp); n->m_pkthdr.len = n->m_len = len; n->m_pkthdr.rcvif = &sc->is_if; /* * Bump up intrface counters. Input packets for * "netstat" include ALL directed, multicast, * and error inputs. For DECnet, only error-free * input packets are counted. */ sc->is_if.if_ipackets++; if (sc->ctrblk.est_bytercvd + len > sc->ctrblk.est_bytercvd) sc->ctrblk.est_bytercvd += len; INCR(sc->ctrblk.est_blokrcvd, 0xffffffff); if (eh.ether_dhost[0] & 1) { if (sc->ctrblk.est_mbytercvd + len > sc->ctrblk.est_mbytercvd) sc->ctrblk.est_mbytercvd += len; INCR(sc->ctrblk.est_mblokrcvd, 0xffffffff); } /* Dispatch this packet */ ether_input(&sc->is_if, &eh, n); } else { /* * Unable to get mbuf for this frame. Discard it. */ INIT_RDESC(rp); } } else { /* * Frame addressed to group address is not one we want. */ INIT_RDESC(rp); } } else { /* * Error summary bit is set. */ sc->is_if.if_ierrors++; if (sc->ctrblk.est_recvfail != 0xffff) { tu_receive_errors(sc, rp->tu_status); } INIT_RDESC(rp); } NEXT_RINDEX(index); sc->rindex = index; } /* end for all packets we own */ } /* * tu_frame_check * See if the frame is one we want. * * Arguments; * dest_addr A pointer to the destination MAC address * sc Driver's softc structure * * Return Values; * TRUE We want this PDU * FALSE We don't want this frame */ static int tu_frame_check(u_char *dest_addr, struct tu_softc *sc) { struct ifnet *ifp = &sc->is_if; int loc; int status; /* * If this is for a unique address, keep it */ if ((dest_addr[0] & 0x01) == 0) return TRUE; /* * If we have a small enough number of group addresses in * our multicast table, perfect filtering is in effect so * we'll want this address. */ if (sc->is_multi.lan_nmulti <= TU_PMULTI) return TRUE; /* * If we are receiving all multicast address or if we are * in promiscuous mode, accept this frame. */ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) return TRUE; /* * We always want the broadcast address */ if (bcmp(dest_addr, etherbroadcastaddr, 6) == 0) return TRUE; /* * Filter this address by checking it against our * list of multicast addresses. */ LAN_FIND_MULTI(&sc->is_multi, dest_addr, loc, status); /* * If we found it and it is in use, accept this frame */ if ((status == LAN_MULTI_FOUND) && (sc->is_multi.lan_mtable[loc].muse > 0)) return TRUE; return FALSE; } /* * tu_receive_errors() * Parse errors occuring on a receive interrupt. Print them to the * console if debug is turned on. * * Arguments: * sc Pointer to the softc structure for this unit * status Status word from RDESC0 * * Return Value: * None. */ static void tu_receive_errors(struct tu_softc *sc, int status) { int unit = sc->is_if.if_unit; sc->ctrblk.est_recvfail++; if (status & TU_OF) { if (tu_error_printfs) printf("tu%d: receive error: overflow\n",unit); sc->ctrblk.est_recvfail_bm |=4 ; } if (status & TU_CE) { if (status & TU_DB) /* Block check error */ sc->ctrblk.est_recvfail_bm |=1 ; else /* Frame error */ sc->ctrblk.est_recvfail_bm |=2 ; if (tu_error_printfs) printf("tu%d: receive error: CRC error\n",unit); } if (status & TU_CS) { sc->ctrblk.est_recvfail_bm |=2 ; if (tu_error_printfs) printf("tu%d: receive error: collision seen\n",unit); } if (status & TU_RTL) { if (tu_error_printfs) printf("tu%d: receive error: frame too long\n",unit); sc->ctrblk.est_recvfail_bm |=4 ; } if ((status & TU_RF) && !(status & TU_OF)) { /* * Runt Frame - will only receive if CSR6 is set. * Meaningless if RDES0 is set. */ if (tu_error_printfs) printf("tu%d: receive error: runt frame\n",unit); sc->ctrblk.est_recvfail_bm |=4 ; } if (status & TU_RLE) { if (tu_error_printfs) printf("tu%d: receive error: length error\n",unit); } } /* * tu_receive_overflow() * Handling for receive overflow conditions. * * Arguments: * sc Pointer to the softc structure for this unit * * Return Value: * None. */ static void tu_receive_overflow(struct tu_softc *sc) { register TUDESC *rp = &sc->rring[sc->rindex]; register int unit = sc->is_if.if_unit; register int index, cmdcsr = 0; /* * Some book-keeping first, before we forget */ sc->is_if.if_ierrors++; if (sc->ctrblk.est_recvfail != 0xffff) sc->ctrblk.est_recvfail++; if (tu_error_printfs) printf("tu%d: receive error: overflow\n",unit); /* * First stop the receive engine within the chip; use a bigger hammer * if it won't listen */ if (!tu_stop_receives(sc)) { printf("tu%d: can't stop receive process: will reset\n", unit); tureset(unit); return; } /* * Discard all outstanding receives: one could compute the CRC on * each packet and only discard those that were bad. Take the simpler * approach for now. */ for (index=sc->rindex; rp->tu_own==TU_HOST; rp=&sc->rring[index]) { INIT_RDESC(rp); NEXT_RINDEX(index); sc->rindex = index; } /* * Start receiving packets again */ TU_WCSR32(sc->tu_csr[6], TU_RCSR32(sc->tu_csr[6]) | TU_CSR6_SR); } /* * tu_stop_receives() * Attempts to stop the Receive process/engine within the 21x4y if * it's not already stopped. * * Arguments: * sc Pointer to the softc structure for this unit * * Return Value: * 1 for success, 0 for failure */ static int tu_stop_receives(struct tu_softc *sc) { register int unit = sc->is_if.if_unit; register int i, stopped = 0; /* * If it's already stop, we're done. */ if (!(TU_RCSR32(sc->tu_csr[5]) & 0xE0000)) return 1; /* * Clear the "start transmission" bit in the command register. */ TU_WCSR32(sc->tu_csr[6], TU_RCSR32(sc->tu_csr[6]) & ~TU_CSR6_SR); /* * Confirm that the engine did actually stop */ for (i=0; i<1000; i++) { if (TU_RCSR32(sc->tu_csr[5]) & TU_CSR5_RPS) { stopped = 1; break; } else { DELAY(1000); } } return stopped; } /* * tu_stop_transmits() * Attempts to stop the Transmit process/engine within the 21x4y if * it's not already stopped. * * Arguments: * sc Pointer to the softc structure for this unit * * Return Value: * 1 for success, 0 for failure */ static int tu_stop_transmits(struct tu_softc *sc) { register int unit = sc->is_if.if_unit; register int i, stopped = 0; /* * If it's already stopped, we're done. */ if (!(TU_RCSR32(sc->tu_csr[5]) & 0x700000)) return 1; /* * Clear the "start transmission" bit in the command register. */ TU_WCSR32(sc->tu_csr[6], TU_RCSR32(sc->tu_csr[6]) & ~TU_CSR6_ST); /* * Confirm that the engine did actually stop */ for (i=0; i<1000; i++) { if (TU_RCSR32(sc->tu_csr[5]) & TU_CSR5_TPS) { stopped = 1; break; } else { DELAY(1000); } } return stopped; } /* * tu_setup_frame() * Takes the information in the multicast table and communicates the * address filtering setup-frame to the device. The decision about * what kind of filtering to use (hash or perfect), is made here. * * This is a synchronous routine. It blocks until the setup frame * completion status is received from chip. * * Arguments * ctlr Pointer to controller struct for the interface. * * Return Value * - EIO * - ENOBUFS * - ESUCCESS */ static int tu_setup_frame(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct tu_softc *sc = tu_softc[unit]; register struct tu_setup *setup_bufr = sc->setup_bufr; register int i,j,status,hash_setup=0; volatile TUDESC *setup_desc; unsigned char *maddr; /* * If we haven't exceeded Tulip's capacity for Perfect-Filtering, then * use that mode. Otherwise we have to switch over to Hash/Imperfect * filtering. */ if (sc->is_multi.lan_nmulti <= TU_PMULTI) { /* * We'll use perfect-filtering. In this case, we'll setup the * 1st entry in the setup buffer as the station address. Second * and subsequent entries will be the user-specified multicast * addresses, if any. The last entry, and unused entries in the * middle, if any, will be setup with broadcast addresses. */ tu_setup_addr(sc->is_addr, &setup_bufr[j=0]); for (i=1, j=0; i <= sc->is_multi.lan_nmulti; i++) { LAN_GET_MULTI(&sc->is_multi, maddr, j); tu_setup_addr(maddr, &setup_bufr[i]); } /* * Fill out the remaining with broadcast addresses. */ while (iis_addr, (struct tu_setup *)((vm_offset_t)setup_bufr+156)); for (i=0, j=0; i < sc->is_multi.lan_nmulti; i++) { LAN_GET_MULTI(&sc->is_multi, maddr, j); tu_setup_hash((void*)maddr, (void*)setup_bufr); } tu_setup_hash(etherbroadcastaddr, (void*)setup_bufr); hash_setup++; } /* * Put the setup-frame into the transmit ring */ status = tu_put_setup(sc, (vm_offset_t)setup_bufr, &setup_desc, hash_setup); /* * tu_put_setup gives us ENOBUFS when it encounters a full * transmit ring. This is indicative of a stuck transmit * ring as is non-completion of the setupframe. */ if ((status == ESUCCESS) || (status == ENOBUFS)) { /* * Setup frame was successfully given to the device. Now have * to wait for the result. This should take on the order of * a few microseconds to complete. (Probably done by now if * there's nothing in the ring.) */ if (status == ESUCCESS) for (i=0; (i < 100000) && (setup_desc->tu_own == TU_TULIP); i++) DELAY(1); /* Wait upto 10th of a second. */ /* * If things went OK, then say so to our caller. Otherwise, we * have to clean things up before we reflect back an error. */ if ( (status == ENOBUFS) || (setup_desc->tu_status != TU_SETUP_OK) ) { /* * The setup frame command must be stuck. * Unfortunately, a reset has to be done to get * the device to accept the setup frame (prior * to retrying pending xmits). * * We have to be careful about blindly calling * tureset since tureset calls this routine. We * use a flag (tu_setup_frame_reset, initialized to * zero in probe) to determined if we called reset * from here before. */ if (!sc->tu_setup_frame_reset) { sc->tu_setup_frame_reset = 1; tureset(unit); status = ESUCCESS; sc->tu_setup_frame_reset = 0; } else { status = EIO; if (tu_error_printfs) printf("tu%d: Device not responding to setup commands.\n", unit); } } } return status; } /* * tu_put_setup() * Puts a setup-frame buffer into the transmit ring. * * Arguments * sc Pointer to the softc structure for this unit * setup_bufr Pointer to the buffer containing the setup frame * setup_desc Pointer to TUDESC* for receiving setup descriptor ref * hash_setup 1 if hash setup is to be done, 0 otherwise. * * Return Value * - EIO * - ENOBUFS * - ESUCCESS */ static int tu_put_setup(struct tu_softc *sc, vm_offset_t setup_bufr, volatile TUDESC **setup_desc, int hash_setup) { register struct ifnet *ifp = &sc->is_if; register int unit = ifp->if_unit; register struct controller *ctlr = tuinfo[unit]; register int ntfree, index = sc->itindex; register TUDESC *tp = &sc->tring[index]; /* * Find out how many free ones we got */ ntfree = TU_NTRING - sc->ntinuse; /* * All we need is one entry in the ring to do this job. But since we * never completely fill the ring up (port requires that at least be * unused for it work), make sure that we have at least two free at * this time. */ if (ntfree > 1) { long bc = TU_SETUP_SIZE; vm_offset_t va = setup_bufr; sglist_t sglp; if (tu_dma_load(bc, va, ctlr, &sc->tsglp[index]) == 0) { printf("tu%d: failed to map setup buffer\n", unit); return EIO; } /* * Map the kernel virtual address to its bus-equivalent */ sglp = sc->tsglp[index]; tp->tu_bfaddr1 = (u_int)sglp->sgp[0].ba; tp->tu_bfsize1 = sglp->sgp[0].bc; /* * Turn on all the appropriate control bits, including the * Hash bit if we are doing a Hash setup. */ tp->tu_control |= TU_SET | TU_TFS | TU_TLS | TU_IC; if (hash_setup) tp->tu_control |= TU_HP; /* * Ready to go -- hand it off to the chip. */ mb(); tp->tu_own = TU_TULIP; /* * Update our index pointers accordingly */ NEXT_TINDEX(index); sc->itindex = index; sc->ntinuse++; sc->ntpendg++; /* * Wake up the chip in case its sleeping. */ TU_WCSR32(sc->tu_csr[1], 1); /* * Return pointer to the setup descriptor for error recovery. */ *setup_desc = tp; return ESUCCESS; } else { printf("tu%d: can't issue setup frame: ring full\n", unit); return ENOBUFS; } } /* * tu_setup_addr() * Sets up the specified ethernet address at the specified location * in the setup buffer. * * Arguments * enet_addr Pointer to the 6-byte ethernet address * setup_ptr Pointer to where the address must be setup in the * setup-frame buffer * Return Value * None */ static void tu_setup_addr(u_char *enet_addr, struct tu_setup *setup_ptr) { /* * Refer to Page 5-62 Figure 5-33 of the V2.0 spec to find * out how this is done! */ setup_ptr->tu_setup_char[0] = enet_addr[0]; setup_ptr->tu_setup_char[1] = enet_addr[1]; setup_ptr->tu_setup_char[4] = enet_addr[2]; setup_ptr->tu_setup_char[5] = enet_addr[3]; setup_ptr->tu_setup_char[8] = enet_addr[4]; setup_ptr->tu_setup_char[9] = enet_addr[5]; } /* * tu_setup_hash() * Sets the hash bit for the specified ethernet address in the * supplied setup buffer. * * Arguments * Address Pointer to the 6-byte ethernet address * setup_bufr Pointer to the setup-frame buffer * * Return Value * None */ static void tu_setup_hash(u_char *Address, u_int *setup_bufr) { register unsigned Crc = 0xffffffff; register unsigned const POLY = 0x04c11db6; register int Bit, Shift, BytesLength; register unsigned Msb, Index; register u_char CurrentByte; /* * This comes straight from Appendix C Hash "C" Routine of the * V2.0 Tulip spec. */ for (BytesLength=0; BytesLength<6; BytesLength++) { CurrentByte = Address[BytesLength]; for (Bit=0; Bit<8; Bit++) { Msb = Crc >> 31; Crc <<= 1; if (Msb ^ (CurrentByte & 1)) { Crc ^= POLY; Crc |= 1; } CurrentByte >>= 1; } } /* * The hash index is given by the upper 9 bits of the CRC taken in * decreasing order of significance: * index<0> = crc<31> * index<1> = crc<30> * . * . * index<9> = crc<23> */ for (Index=0,Bit=23,Shift=8; Shift >= 0; Bit++,Shift--) Index |= (((Crc>>Bit) & 1) << Shift); /* * Set the target bit in our hash table now. */ setup_bufr[Index/16] |= 1 << (Index%16); } /* * tu_dma_load() * Convenient routine for calling the dma_map_load routine. If *sglp_p * is initialized to NULL prior to the call, then an alloc will be done * prior to the load. * * Arguments * bc byte count to map/translate from virtual-to-physical * va virtual address to map (must be in kernel space) * ctlr pointer to controller struct for the interface. * sglp_p pointer to an sg-list ptr for receiving mapping info * * Return Value * 0 - Failure * 1 - Success */ static int tu_dma_load(long bc, vm_offset_t va, struct controller *ctlr, sglist_t *sglp_p) { register long rbc; register int unit = ctlr->ctlr_num; register int flags = DMA_IN | DMA_OUT | DMA_ALL; /* * Simply pass on the arguments given to us. */ rbc = dma_map_load(bc, va, (struct proc*)0, ctlr, sglp_p, bc, flags); /* * Some sanity checks before declaring that the call succeeded */ if ((rbc != bc) || (*sglp_p == NULL) || ((*sglp_p)->sgp == NULL)) { printf("tu%d: dma mapping [alloc-load] failure\n", unit); return 0; } else return 1; } /* * Routines to read and write to the MII registers. */ static u_long tu_getfrom_mii(struct tu_softc *sc, u_long command) { TU_WCSR32(sc->tu_csr[9], command); DELAY(1); TU_WCSR32(sc->tu_csr[9], command | TU_MII_MDC); DELAY(1); return ((TU_RCSR32(sc->tu_csr[9]) >> 19) & 1); } static void tu_sendto_mii(struct tu_softc *sc, u_long command, u_long data) { u_long j; j = (data & 1) << 17; TU_WCSR32(sc->tu_csr[9], command | j); DELAY(1); TU_WCSR32(sc->tu_csr[9], command | TU_MII_MDC | j); DELAY(1); } static u_long tu_mii_rdata(struct tu_softc *sc) { u_long i, tmp = 0; for (i=0; i<16; i++) { tmp <<= 1; tmp |= tu_getfrom_mii(sc, TU_MII_MRD | TU_MII_RD); } return tmp; } static void tu_mii_wdata(struct tu_softc *sc, u_long data, u_long len) { u_long i; for (i=0; i>= 1; } } static u_long tu_mii_swap(u_long data, u_long len) { u_long i, tmp = 0; for (i=0; i>= 1; } return tmp; } static void tu_mii_turnaround(struct tu_softc *sc, u_long rw) { if (rw == TU_MII_STARTWR) { tu_sendto_mii(sc, TU_MII_MWR | TU_MII_WR, 1); tu_sendto_mii(sc, TU_MII_MWR | TU_MII_WR, 0); } else { tu_getfrom_mii(sc, TU_MII_MRD | TU_MII_RD); } } static u_short tu_mii_rd(struct tu_softc *sc, u_char phyreg) { tu_mii_wdata(sc, TU_MII_PREAMBLE, 34); tu_mii_wdata(sc, TU_MII_STARTRD, 4); tu_mii_wdata(sc, tu_mii_swap(sc->mii_phyaddr, 5), 5); tu_mii_wdata(sc, tu_mii_swap(phyreg, 5), 5); tu_mii_turnaround(sc, TU_MII_STARTRD); return (u_short)tu_mii_rdata(sc); } static void tu_mii_wr(struct tu_softc *sc, u_char phyreg, u_long data) { tu_mii_wdata(sc, TU_MII_PREAMBLE, 34); tu_mii_wdata(sc, TU_MII_STARTWR, 4); tu_mii_wdata(sc, tu_mii_swap(sc->mii_phyaddr, 5), 5); tu_mii_wdata(sc, tu_mii_swap(phyreg, 5), 5); tu_mii_turnaround(sc, TU_MII_STARTWR); tu_mii_wdata(sc, tu_mii_swap(data, 16), 16); } static int tu_flags_to_port(struct tu_softc *sc) { int port; if (sc->tu_flags.aui_mode) port = TU_PORT_AUI; else if (sc->tu_flags.bnc_mode) port = TU_PORT_BNC; else if (sc->tu_flags.fx_mode) port = sc->tu_flags.full_duplex ? TU_PORT_100FXF : TU_PORT_100FX; else if (sc->tu_flags.fast_speed) port = sc->tu_flags.full_duplex ? TU_PORT_100F : TU_PORT_100; else port = sc->tu_flags.full_duplex ? TU_PORT_FDX : TU_PORT_UTP; return port; } static void tu_flags_to_ability(struct tu_softc *sc) { sc->tu_auto_ability = 0xF; if (!sc->tu_flags.fast_speed) sc->tu_auto_ability >>= 2; if (!sc->tu_flags.full_duplex) sc->tu_auto_ability >>= 1; } static void tu_ability_to_flags(struct tu_softc *sc, u_short ability) { sc->tu_flags.fast_speed = 0; sc->tu_flags.full_duplex = 0; if (ability & 8) { sc->tu_flags.fast_speed = 1; sc->tu_flags.full_duplex = 1; } else if (ability & 4) sc->tu_flags.fast_speed = 1; else if (ability & 2) sc->tu_flags.full_duplex = 1; } /* * tu_auto_negotiate: * * This routine implements the Auto Negotiation state machine. The * routine/state-machine is activated initially from tuinit() via * the timeout() mechanism. Thereafter, the routine schedules itself * every 500 milliseconds --- we consider this one "tick" for the * state machine. * * Once it is started up, the state machine will remain active until * auto negotiation is explicitly disabled from the user-level (via * lan_config). In particular, there is no logic in place today to * terminate or give up if auto negotiation does not complete in a * certain amount of time. * * Arguments * sc Pointer to the softc structure for this unit * * sc->tu_flags.{fast_speed, full_duplex} used to advertise cability * sc->tu_auto_ticks must be initialized properly. * sc->tu_auto_state must be initialized properly. * * Return Value * None */ static void tu_auto_negotiate(struct tu_softc *sc) { volatile u_short mii_sr, mii_pr; int s, next_state, port, unit = sc->is_if.if_unit; u_short partner, common_ability; u_char link, mii = sc->mii_phyaddr; u_int cmdcsr, csr12, csr14; /* * Sync up with the rest of the driver */ s = splimp(); simple_lock(&sc->lk_tu_softc); /* * Assume no state change by default */ next_state = sc->tu_auto_state; switch (sc->tu_auto_state) { case TU_AUTO_STATE_INITIAL: /* * Ensure transmit and receive engines are stopped */ if (!tu_stop_receives(sc) || !tu_stop_transmits(sc)) { tureset(unit); break; } /* * In this state, we simply start things (over again) by * doing a soft reset of the PHY. For the 21143, we need * to be in 10BaseT mode as required by the spec. */ if (mii) tu_mii_wr(sc, TU_MII_CR, TU_MII_CR_RST); else tu_select_port(sc, TU_PORT_UTP); next_state = TU_AUTO_STATE_RESET; break; case TU_AUTO_STATE_RESET: /* * In this state the PHY is being reset. Need to wait at * least one second for the reset to complete. If the reset * doesn't complete in that time, try again (forever). */ if (mii && (tu_mii_rd(sc, TU_MII_CR) & TU_MII_CR_RST)) { if (sc->tu_auto_ticks > 1) { next_state = TU_AUTO_STATE_INITIAL; break; } } /* * PHY reset was successful. Reset our ability in the event * we had negotiated something other than our capability (last * time we were up & running). */ tu_ability_to_flags(sc, sc->tu_auto_ability); /* * Setup the ability register before we enable and (re)start * auto negotiation. */ if (mii) { tu_mii_wr(sc, TU_MII_AR, (sc->tu_auto_ability << 5)|1); tu_mii_wr(sc, TU_MII_CR, TU_MII_CR_ANE | TU_MII_CR_RAN); } else { /* * The 100BaseTX (Half- and Full-Duplex), and 10BaseT * Half-Duplex ability (which we *always* set) is * specified in CSR14. */ csr14 = TU_RCSR32(sc->tu_csr[14]) & 0xFFFF; csr14 |= ((sc->tu_auto_ability & 0xC) << 14) | 0x40; /* * The 10BaseT Full-Duplex ability is specified via * the FD bit in CSR6. We need to make sure that the * PCS function is enabled during auto-negotiation. */ cmdcsr = TU_CSR6_PCS; if (sc->tu_auto_ability & 2) cmdcsr |= TU_CSR6_FD; TU_WCSR32(sc->tu_csr[6], cmdcsr); /* * Kick off auto-negotiation and clear the status * register (which will contain the partner info, etc.) */ TU_WCSR32(sc->tu_csr[14], csr14 | TU_CSR14_ANE); TU_WCSR32(sc->tu_csr[12], 0); } next_state = TU_AUTO_STATE_RESTART; break; case TU_AUTO_STATE_RESTART: /* * In this state we wait for auto negotiation to start. The * requirement here is to wait 1.5 seconds. */ if (mii && (tu_mii_rd(sc, TU_MII_CR) & TU_MII_CR_RAN)) { if (sc->tu_auto_ticks > 2) { /* * Didn't happen. Start things over again. */ next_state = TU_AUTO_STATE_INITIAL; } else { /* Still some time: remain in current state */ } } else { /* * Auto negotiation started successfully. Transitition. */ next_state = TU_AUTO_STATE_PENDING; } break; case TU_AUTO_STATE_PENDING: /* * In this state we wait for auto negotiation to complete. The * requirement here is that we give the hardware a couple of * seconds to wrap things up. */ if (mii) { mii_sr = tu_mii_rd(sc, TU_MII_SR); link = (mii_sr & 0x0034) == 0x0024; partner = tu_mii_rd(sc, TU_MII_PR); } else { csr12 = TU_RCSR32(sc->tu_csr[12]); link = (csr12 & 0xF000) == 0xD000; partner = (csr12 & 0xFFFF0000) >> 16; } /* * Decide whether or not everthing is alright with link, * common ability, etc. and then either declare link-up * or start things over again. * * If link-down, or remote-fault, or partner's ability is * 0 or not 802.3, then this completion isn't acceptable. * Restart. */ if (!link || !(partner & 0x1E0) || ((partner & 0x1F) != 1)) { if (sc->tu_auto_ticks > 8) { /* * Didn't complete and more than 4 seconds have * gone by. Start things over again. */ next_state = TU_AUTO_STATE_INITIAL; } else { /* Still some time: remain in current state */ } break; } /* * We get the common ability by AND'ing our abiity with that * of the partner. */ common_ability = sc->tu_auto_ability & ((partner & 0x1E0) >> 5); if (common_ability) { /* * Setup up sc->tu_flags.{fast_speed, full_duplex} */ tu_ability_to_flags(sc, common_ability); } else { /* * If we have nothing in common, too bad. */ next_state = TU_AUTO_STATE_INITIAL; break; } cmdcsr = tu_select_port(sc, tu_flags_to_port(sc)); /* * Again, we must have the PCS functionality enabled during * auto-negotiation. If we don't, then we won't be able to * detect the 100Mbps link. */ if (!mii) cmdcsr |= TU_CSR6_PCS; /* * All set; start the chip. */ TU_WCSR32(sc->tu_csr[6], cmdcsr | TU_CSR6_ST | TU_CSR6_SR); /* * Good to inform the user what we finally settled on */ printf("tu%d: link up: negotiated %s: %s duplex\n",unit, sc->tu_flags.fast_speed ? "100BaseTX" : "10BaseT", sc->tu_flags.full_duplex ? "full" : "half"); next_state = TU_AUTO_STATE_LINK_UP; break; case TU_AUTO_STATE_LINK_UP: /* * This is the steady state. We monitor a simple rx/tx activity * flag first to find out if packets are flowing OK. This way * we don't have to use the expensive MII routine all the time. */ /* * Didn't see a packet come in or go out in the last 1/2 second. */ if (mii) link = tu_mii_rd(sc, TU_MII_SR) & TU_MII_SR_LKS; else if (sc->tu_flags.fast_speed) link = !(TU_RCSR32(sc->tu_csr[12]) & TU_CSR12_LS100); else link = !(TU_RCSR32(sc->tu_csr[12]) & TU_CSR12_LS10); if (!link) { if (sc->tu_auto_ticks > 2) { next_state = TU_AUTO_STATE_INITIAL; printf("tu%d: link down\n", unit); } } break; default: printf("tu%d: auto negotiation (internal driver) error\n",unit); tureset(unit); } /* * If there was a state transition, then register that and reset the * timer for the new state. Otherwise, simply bump up the timer count. */ if (next_state != sc->tu_auto_state) { sc->tu_auto_state = next_state; sc->tu_auto_ticks = 1; } else sc->tu_auto_ticks++; /* * Schedule next tick, unlock and we're done. */ if ((next_state == sc->tu_auto_state) && (next_state == TU_AUTO_STATE_LINK_UP)) timeout((void(*)(void *))tu_auto_negotiate, sc, 2*hz); else timeout((void(*)(void *))tu_auto_negotiate, sc, hz/2); simple_unlock(&sc->lk_tu_softc); splx(s); } /* * Name: tu_freemem(unit) * * Arguments: * unit: The unit number for the adapter whose * data structures we are freeing * * Functional Description: * This routine frees all memory which has been * allocated thus far for the device. It also * releases any DMA resources. * * Return Code: * None */ static void tu_freemem(int unit) { int i; struct tu_softc *sc = tu_softc[unit]; if (sc != NULL) { /* * Deregister our interrupt handler from the * interrupt dispatcher */ if (sc->hid) handler_del(sc->hid); /* * Deallocate the ether driver structure */ if (sc->is_ed != NULL) if_dealloc(sc->is_ed); /* * Free adapter data structures */ if (sc->setup_bufr != NULL) FREE(sc->setup_bufr, M_DEVBUF); if (sc->tring != NULL) { FREE(sc->tring, M_DEVBUF); dma_map_dealloc(sc->tring_sglp); } if (sc->rring != NULL) { FREE(sc->rring, M_DEVBUF); dma_map_dealloc(sc->rring_sglp); } /* * Clean up Transmit Queue and free DMA re