189099Sfjoe/* $NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */ 289099Sfjoe 3119418Sobrien#include <sys/cdefs.h> 4119418Sobrien__FBSDID("$FreeBSD$"); 5119418Sobrien 689099Sfjoe/*- 789099Sfjoe * Copyright (c) 1994, 1995, 1998 The NetBSD Foundation, Inc. 889099Sfjoe * All rights reserved. 989099Sfjoe * 1089099Sfjoe * This code is derived from software contributed to The NetBSD Foundation 1189099Sfjoe * by Ignatios Souvatzis. 1289099Sfjoe * 1389099Sfjoe * Redistribution and use in source and binary forms, with or without 1489099Sfjoe * modification, are permitted provided that the following conditions 1589099Sfjoe * are met: 1689099Sfjoe * 1. Redistributions of source code must retain the above copyright 1789099Sfjoe * notice, this list of conditions and the following disclaimer. 1889099Sfjoe * 2. Redistributions in binary form must reproduce the above copyright 1989099Sfjoe * notice, this list of conditions and the following disclaimer in the 2089099Sfjoe * documentation and/or other materials provided with the distribution. 2189099Sfjoe * 2289099Sfjoe * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2389099Sfjoe * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2489099Sfjoe * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2589099Sfjoe * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2689099Sfjoe * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2789099Sfjoe * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2889099Sfjoe * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2989099Sfjoe * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3089099Sfjoe * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3189099Sfjoe * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3289099Sfjoe * POSSIBILITY OF SUCH DAMAGE. 3389099Sfjoe */ 3489099Sfjoe 3589099Sfjoe/* 3689099Sfjoe * Chip core driver for the SMC90c26 / SMC90c56 (and SMC90c66 in '56 3789099Sfjoe * compatibility mode) boards 3889099Sfjoe */ 3989099Sfjoe 4089099Sfjoe/* #define CMSOFTCOPY */ 4189099Sfjoe#define CMRETRANSMIT /**/ 42109771Sfjoe/* #define CM_DEBUG */ 4389099Sfjoe 4489099Sfjoe#include <sys/param.h> 4589099Sfjoe#include <sys/systm.h> 4689099Sfjoe#include <sys/sockio.h> 4789099Sfjoe#include <sys/mbuf.h> 4889099Sfjoe#include <sys/module.h> 4989099Sfjoe#include <sys/kernel.h> 5089099Sfjoe#include <sys/socket.h> 5189099Sfjoe#include <sys/syslog.h> 5289099Sfjoe#include <sys/bus.h> 5389099Sfjoe 5489099Sfjoe#include <machine/bus.h> 5589099Sfjoe#include <sys/rman.h> 5689099Sfjoe#include <machine/resource.h> 5789099Sfjoe 5889099Sfjoe#include <net/if.h> 5989099Sfjoe#include <net/if_dl.h> 6089099Sfjoe#include <net/if_types.h> 6189099Sfjoe#include <net/if_arc.h> 6289099Sfjoe 6389099Sfjoe#include <dev/cm/smc90cx6reg.h> 6489099Sfjoe#include <dev/cm/smc90cx6var.h> 6589099Sfjoe 6689099SfjoeMODULE_DEPEND(if_cm, arcnet, 1, 1, 1); 6789099Sfjoe 6889099Sfjoe/* these should be elsewhere */ 6989099Sfjoe 7089099Sfjoe#define ARC_MIN_LEN 1 7189099Sfjoe#define ARC_MIN_FORBID_LEN 254 7289099Sfjoe#define ARC_MAX_FORBID_LEN 256 7389099Sfjoe#define ARC_MAX_LEN 508 7489099Sfjoe#define ARC_ADDR_LEN 1 7589099Sfjoe 7689099Sfjoe/* for watchdog timer. This should be more than enough. */ 7789099Sfjoe#define ARCTIMEOUT (5*IFNET_SLOWHZ) 7889099Sfjoe 7989099Sfjoedevclass_t cm_devclass; 8089099Sfjoe 8189099Sfjoe/* 8289099Sfjoe * This currently uses 2 bufs for tx, 2 for rx 8389099Sfjoe * 8489099Sfjoe * New rx protocol: 8589099Sfjoe * 8689099Sfjoe * rx has a fillcount variable. If fillcount > (NRXBUF-1), 8789099Sfjoe * rx can be switched off from rx hard int. 8889099Sfjoe * Else rx is restarted on the other receiver. 8989099Sfjoe * rx soft int counts down. if it is == (NRXBUF-1), it restarts 9089099Sfjoe * the receiver. 9189099Sfjoe * To ensure packet ordering (we need that for 1201 later), we have a counter 9289099Sfjoe * which is incremented modulo 256 on each receive and a per buffer 9389099Sfjoe * variable, which is set to the counter on filling. The soft int can 9489099Sfjoe * compare both values to determine the older packet. 9589099Sfjoe * 9689099Sfjoe * Transmit direction: 9789099Sfjoe * 9889099Sfjoe * cm_start checks tx_fillcount 9989099Sfjoe * case 2: return 10089099Sfjoe * 10189099Sfjoe * else fill tx_act ^ 1 && inc tx_fillcount 10289099Sfjoe * 10389099Sfjoe * check tx_fillcount again. 104148887Srwatson * case 2: set IFF_DRV_OACTIVE to stop arc_output from filling us. 10589099Sfjoe * case 1: start tx 10689099Sfjoe * 107159529Sfjoe * tint clears IFF_OACTIVE, decrements and checks tx_fillcount 10889099Sfjoe * case 1: start tx on tx_act ^ 1, softcall cm_start 10989099Sfjoe * case 0: softcall cm_start 11089099Sfjoe * 11189099Sfjoe * #define fill(i) get mbuf && copy mbuf to chip(i) 11289099Sfjoe */ 11389099Sfjoe 11492739Salfredvoid cm_init(void *); 115159529Sfjoestatic void cm_init_locked(struct cm_softc *); 116159529Sfjoestatic void cm_reset_locked(struct cm_softc *); 11792739Salfredvoid cm_start(struct ifnet *); 118159529Sfjoevoid cm_start_locked(struct ifnet *); 11992739Salfredint cm_ioctl(struct ifnet *, unsigned long, caddr_t); 120199559Sjhbvoid cm_watchdog(void *); 121159529Sfjoevoid cm_srint_locked(void *vsc); 122159529Sfjoestatic void cm_tint_locked(struct cm_softc *, int); 123159529Sfjoevoid cm_reconwatch_locked(void *); 12489099Sfjoe 12589099Sfjoe/* 12689099Sfjoe * Release all resources 12789099Sfjoe */ 12889099Sfjoevoid 12989099Sfjoecm_release_resources(dev) 13089099Sfjoe device_t dev; 13189099Sfjoe{ 13289099Sfjoe struct cm_softc *sc = device_get_softc(dev); 13389099Sfjoe 134159529Sfjoe if (sc->port_res != NULL) { 13589099Sfjoe bus_release_resource(dev, SYS_RES_IOPORT, 136159529Sfjoe 0, sc->port_res); 137159529Sfjoe sc->port_res = NULL; 13889099Sfjoe } 139159529Sfjoe if (sc->mem_res != NULL) { 14089099Sfjoe bus_release_resource(dev, SYS_RES_MEMORY, 141159529Sfjoe 0, sc->mem_res); 142159529Sfjoe sc->mem_res = NULL; 14389099Sfjoe } 144159529Sfjoe if (sc->irq_res != NULL) { 14589099Sfjoe bus_release_resource(dev, SYS_RES_IRQ, 146159529Sfjoe 0, sc->irq_res); 147159529Sfjoe sc->irq_res = NULL; 14889099Sfjoe } 14989099Sfjoe} 15089099Sfjoe 15189099Sfjoeint 152121816Sbrookscm_attach(dev) 153121816Sbrooks device_t dev; 15489099Sfjoe{ 155121816Sbrooks struct cm_softc *sc = device_get_softc(dev); 156147256Sbrooks struct ifnet *ifp; 15789099Sfjoe u_int8_t linkaddress; 15889099Sfjoe 159147256Sbrooks ifp = sc->sc_ifp = if_alloc(IFT_ARCNET); 160159529Sfjoe if (ifp == NULL) 161147256Sbrooks return (ENOSPC); 162147256Sbrooks 16389099Sfjoe /* 16489099Sfjoe * read the arcnet address from the board 16589099Sfjoe */ 16689099Sfjoe GETREG(CMRESET); 16789099Sfjoe do { 16889099Sfjoe DELAY(200); 16989099Sfjoe } while (!(GETREG(CMSTAT) & CM_POR)); 17089099Sfjoe linkaddress = GETMEM(CMMACOFF); 17189099Sfjoe 17289099Sfjoe /* clear the int mask... */ 17389099Sfjoe sc->sc_intmask = 0; 17489099Sfjoe PUTREG(CMSTAT, 0); 17589099Sfjoe 17689099Sfjoe PUTREG(CMCMD, CM_CONF(CONF_LONG)); 17789099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); 17889099Sfjoe sc->sc_recontime = sc->sc_reconcount = 0; 17989099Sfjoe 18089099Sfjoe /* 18189099Sfjoe * set interface to stopped condition (reset) 18289099Sfjoe */ 183159529Sfjoe cm_stop_locked(sc); 18489099Sfjoe 185121752Sbrooks ifp->if_softc = sc; 186121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 187121752Sbrooks ifp->if_output = arc_output; 188121752Sbrooks ifp->if_start = cm_start; 189121752Sbrooks ifp->if_ioctl = cm_ioctl; 190121752Sbrooks ifp->if_init = cm_init; 191121752Sbrooks /* XXX IFQ_SET_READY(&ifp->if_snd); */ 192207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 193159529Sfjoe ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 19489099Sfjoe 195121752Sbrooks arc_ifattach(ifp, linkaddress); 19689099Sfjoe 19789099Sfjoe#ifdef CMSOFTCOPY 198121752Sbrooks sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc); 199121752Sbrooks sc->sc_txcookie = softintr_establish(IPL_SOFTNET, 200121752Sbrooks (void (*)(void *))cm_start, ifp); 20189099Sfjoe#endif 20289099Sfjoe 203159529Sfjoe callout_init_mtx(&sc->sc_recon_ch, &sc->sc_mtx, 0); 204199559Sjhb callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0); 20589099Sfjoe 206109771Sfjoe if_printf(ifp, "link addr 0x%02x (%d)\n", linkaddress, linkaddress); 20789099Sfjoe return 0; 20889099Sfjoe} 20989099Sfjoe 21089099Sfjoe/* 21189099Sfjoe * Initialize device 21289099Sfjoe * 21389099Sfjoe */ 21489099Sfjoevoid 21589099Sfjoecm_init(xsc) 21689099Sfjoe void *xsc; 21789099Sfjoe{ 21889099Sfjoe struct cm_softc *sc = (struct cm_softc *)xsc; 21989099Sfjoe 220159529Sfjoe CM_LOCK(sc); 221159529Sfjoe cm_init_locked(sc); 222159529Sfjoe CM_UNLOCK(sc); 223159529Sfjoe} 22489099Sfjoe 225159529Sfjoestatic void 226159529Sfjoecm_init_locked(struct cm_softc *sc) 227159529Sfjoe{ 228159529Sfjoe struct ifnet *ifp = sc->sc_ifp; 229159529Sfjoe 230148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 231148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 232159529Sfjoe cm_reset_locked(sc); 23389099Sfjoe } 23489099Sfjoe} 23589099Sfjoe 23689099Sfjoe/* 23789099Sfjoe * Reset the interface... 23889099Sfjoe * 239159529Sfjoe * Assumes that it is called with sc_mtx held 24089099Sfjoe */ 24189099Sfjoevoid 242159529Sfjoecm_reset_locked(sc) 24389099Sfjoe struct cm_softc *sc; 24489099Sfjoe{ 24589099Sfjoe struct ifnet *ifp; 24689099Sfjoe int linkaddress; 24789099Sfjoe 248147256Sbrooks ifp = sc->sc_ifp; 24989099Sfjoe 25089099Sfjoe#ifdef CM_DEBUG 251104251Sbrooks if_printf(ifp, "reset\n"); 25289099Sfjoe#endif 25389099Sfjoe /* stop and restart hardware */ 25489099Sfjoe 25589099Sfjoe GETREG(CMRESET); 25689099Sfjoe do { 25789099Sfjoe DELAY(200); 25889099Sfjoe } while (!(GETREG(CMSTAT) & CM_POR)); 25989099Sfjoe 26089099Sfjoe linkaddress = GETMEM(CMMACOFF); 26189099Sfjoe 26289099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 2) 263104251Sbrooks if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n", 264104251Sbrooks linkaddress, linkaddress); 26589099Sfjoe#endif 26689099Sfjoe 26789099Sfjoe /* tell the routing level about the (possibly changed) link address */ 26889099Sfjoe arc_storelladdr(ifp, linkaddress); 26989099Sfjoe arc_frag_init(ifp); 27089099Sfjoe 27189099Sfjoe /* POR is NMI, but we need it below: */ 27289099Sfjoe sc->sc_intmask = CM_RECON|CM_POR; 27389099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 27489099Sfjoe PUTREG(CMCMD, CM_CONF(CONF_LONG)); 27589099Sfjoe 27689099Sfjoe#ifdef CM_DEBUG 277104251Sbrooks if_printf(ifp, "reset: chip configured, status=0x%02x\n", 278104251Sbrooks GETREG(CMSTAT)); 27989099Sfjoe#endif 28089099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); 28189099Sfjoe 28289099Sfjoe#ifdef CM_DEBUG 283104251Sbrooks if_printf(ifp, "reset: bits cleared, status=0x%02x\n", 284104251Sbrooks GETREG(CMSTAT)); 28589099Sfjoe#endif 28689099Sfjoe 28789099Sfjoe sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS; 28889099Sfjoe 28989099Sfjoe /* start receiver */ 29089099Sfjoe 29189099Sfjoe sc->sc_intmask |= CM_RI; 29289099Sfjoe sc->sc_rx_fillcount = 0; 29389099Sfjoe sc->sc_rx_act = 2; 29489099Sfjoe 29589099Sfjoe PUTREG(CMCMD, CM_RXBC(2)); 29689099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 29789099Sfjoe 29889099Sfjoe#ifdef CM_DEBUG 299104251Sbrooks if_printf(ifp, "reset: started receiver, status=0x%02x\n", 300104251Sbrooks GETREG(CMSTAT)); 30189099Sfjoe#endif 30289099Sfjoe 30389099Sfjoe /* and init transmitter status */ 30489099Sfjoe sc->sc_tx_act = 0; 30589099Sfjoe sc->sc_tx_fillcount = 0; 30689099Sfjoe 307148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 308148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 30989099Sfjoe 310199559Sjhb callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc); 311159529Sfjoe cm_start_locked(ifp); 31289099Sfjoe} 31389099Sfjoe 31489099Sfjoe/* 31589099Sfjoe * Take interface offline 31689099Sfjoe */ 31789099Sfjoevoid 318159529Sfjoecm_stop_locked(sc) 31989099Sfjoe struct cm_softc *sc; 32089099Sfjoe{ 32189099Sfjoe /* Stop the interrupts */ 32289099Sfjoe PUTREG(CMSTAT, 0); 32389099Sfjoe 32489099Sfjoe /* Stop the interface */ 32589099Sfjoe GETREG(CMRESET); 32689099Sfjoe 32789099Sfjoe /* Stop watchdog timer */ 328199559Sjhb callout_stop(&sc->sc_watchdog_timer); 329199559Sjhb sc->sc_timer = 0; 33089099Sfjoe} 33189099Sfjoe 332159529Sfjoevoid 333159529Sfjoecm_start(struct ifnet *ifp) 334159529Sfjoe{ 335159529Sfjoe struct cm_softc *sc = ifp->if_softc; 336159529Sfjoe 337159529Sfjoe CM_LOCK(sc); 338159529Sfjoe cm_start_locked(ifp); 339159529Sfjoe CM_UNLOCK(sc); 340159529Sfjoe} 341159529Sfjoe 34289099Sfjoe/* 34389099Sfjoe * Start output on interface. Get another datagram to send 34489099Sfjoe * off the interface queue, and copy it to the 34589099Sfjoe * interface becore starting the output 34689099Sfjoe * 347159529Sfjoe * Assumes that sc_mtx is held 34889099Sfjoe */ 34989099Sfjoevoid 350159529Sfjoecm_start_locked(ifp) 35189099Sfjoe struct ifnet *ifp; 35289099Sfjoe{ 35389099Sfjoe struct cm_softc *sc = ifp->if_softc; 354159529Sfjoe struct mbuf *m, *mp; 35589099Sfjoe 35689099Sfjoe int cm_ram_ptr; 357159529Sfjoe int len, tlen, offset, buffer; 35889099Sfjoe#ifdef CMTIMINGS 35989099Sfjoe u_long copystart, lencopy, perbyte; 36089099Sfjoe#endif 36189099Sfjoe 36289099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 3) 363104251Sbrooks if_printf(ifp, "start(%p)\n", ifp); 36489099Sfjoe#endif 36589099Sfjoe 366148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 36789099Sfjoe return; 36889099Sfjoe 369159529Sfjoe if (sc->sc_tx_fillcount >= 2) 37089099Sfjoe return; 37189099Sfjoe 37289099Sfjoe m = arc_frag_next(ifp); 37389099Sfjoe buffer = sc->sc_tx_act ^ 1; 37489099Sfjoe 37589099Sfjoe if (m == 0) 37689099Sfjoe return; 37789099Sfjoe 37889099Sfjoe#ifdef CM_DEBUG 37989099Sfjoe if (m->m_len < ARC_HDRLEN) 38089099Sfjoe m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */ 381104251Sbrooks if_printf(ifp, "start: filling %d from %d to %d type %d\n", 382104251Sbrooks buffer, mtod(m, u_char *)[0], 38389099Sfjoe mtod(m, u_char *)[1], mtod(m, u_char *)[2]); 38489099Sfjoe#else 38589099Sfjoe if (m->m_len < 2) 38689099Sfjoe m = m_pullup(m, 2); 38789099Sfjoe#endif 388109771Sfjoe cm_ram_ptr = buffer * 512; 38989099Sfjoe 39089099Sfjoe if (m == 0) 39189099Sfjoe return; 39289099Sfjoe 39389099Sfjoe /* write the addresses to RAM and throw them away */ 39489099Sfjoe 39589099Sfjoe /* 39689099Sfjoe * Hardware does this: Yet Another Microsecond Saved. 39789099Sfjoe * (btw, timing code says usually 2 microseconds) 39889099Sfjoe * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]); 39989099Sfjoe */ 40089099Sfjoe 40189099Sfjoe PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]); 40289099Sfjoe m_adj(m, 2); 40389099Sfjoe 40489099Sfjoe /* get total length left at this point */ 40589099Sfjoe tlen = m->m_pkthdr.len; 40689099Sfjoe if (tlen < ARC_MIN_FORBID_LEN) { 40789099Sfjoe offset = 256 - tlen; 40889099Sfjoe PUTMEM(cm_ram_ptr + 2, offset); 40989099Sfjoe } else { 41089099Sfjoe PUTMEM(cm_ram_ptr + 2, 0); 41189099Sfjoe if (tlen <= ARC_MAX_FORBID_LEN) 41289099Sfjoe offset = 255; /* !!! */ 41389099Sfjoe else { 41489099Sfjoe if (tlen > ARC_MAX_LEN) 41589099Sfjoe tlen = ARC_MAX_LEN; 41689099Sfjoe offset = 512 - tlen; 41789099Sfjoe } 41889099Sfjoe PUTMEM(cm_ram_ptr + 3, offset); 41989099Sfjoe 42089099Sfjoe } 42189099Sfjoe cm_ram_ptr += offset; 42289099Sfjoe 42389099Sfjoe /* lets loop through the mbuf chain */ 42489099Sfjoe 42589099Sfjoe for (mp = m; mp; mp = mp->m_next) { 42689099Sfjoe if ((len = mp->m_len)) { /* YAMS */ 42789099Sfjoe bus_space_write_region_1( 42889099Sfjoe rman_get_bustag(sc->mem_res), 42989099Sfjoe rman_get_bushandle(sc->mem_res), 43089099Sfjoe cm_ram_ptr, mtod(mp, caddr_t), len); 43189099Sfjoe 43289099Sfjoe cm_ram_ptr += len; 43389099Sfjoe } 43489099Sfjoe } 43589099Sfjoe 43689099Sfjoe sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0; 43789099Sfjoe sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5; 43889099Sfjoe 43989099Sfjoe if (++sc->sc_tx_fillcount > 1) { 44089099Sfjoe /* 44189099Sfjoe * We are filled up to the rim. No more bufs for the moment, 44289099Sfjoe * please. 44389099Sfjoe */ 444148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 44589099Sfjoe } else { 44689099Sfjoe#ifdef CM_DEBUG 447104251Sbrooks if_printf(ifp, "start: starting transmitter on buffer %d\n", 448104251Sbrooks buffer); 44989099Sfjoe#endif 45089099Sfjoe /* Transmitter was off, start it */ 45189099Sfjoe sc->sc_tx_act = buffer; 45289099Sfjoe 45389099Sfjoe /* 45489099Sfjoe * We still can accept another buf, so don't: 455148887Srwatson * ifp->if_drv_flags |= IFF_DRV_OACTIVE; 45689099Sfjoe */ 45789099Sfjoe sc->sc_intmask |= CM_TA; 45889099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 45989099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 46089099Sfjoe 461199559Sjhb sc->sc_timer = ARCTIMEOUT; 46289099Sfjoe } 46389099Sfjoe m_freem(m); 46489099Sfjoe 46589099Sfjoe /* 46689099Sfjoe * After 10 times reading the docs, I realized 46789099Sfjoe * that in the case the receiver NAKs the buffer request, 46889099Sfjoe * the hardware retries till shutdown. 46989099Sfjoe * This is integrated now in the code above. 47089099Sfjoe */ 471159529Sfjoe} 47289099Sfjoe 473159529Sfjoe#ifdef CMSOFTCOPY 474159529Sfjoevoid 475159529Sfjoecm_srint(void *vsc) 476159529Sfjoe{ 477159529Sfjoe struct cm_softc *sc = (struct cm_softc *)vsc; 478159529Sfjoe 479159529Sfjoe CM_LOCK(sc); 480159529Sfjoe cm_srint_locked(vsc); 481159529Sfjoe CM_UNLOCK(sc); 48289099Sfjoe} 483159529Sfjoe#endif 48489099Sfjoe 48589099Sfjoe/* 48689099Sfjoe * Arcnet interface receiver soft interrupt: 48789099Sfjoe * get the stuff out of any filled buffer we find. 48889099Sfjoe */ 48989099Sfjoevoid 490159529Sfjoecm_srint_locked(vsc) 49189099Sfjoe void *vsc; 49289099Sfjoe{ 49389099Sfjoe struct cm_softc *sc = (struct cm_softc *)vsc; 494159529Sfjoe int buffer, len, offset, type; 49589099Sfjoe int cm_ram_ptr; 49689099Sfjoe struct mbuf *m; 49789099Sfjoe struct arc_header *ah; 49889099Sfjoe struct ifnet *ifp; 49989099Sfjoe 500147256Sbrooks ifp = sc->sc_ifp; 50189099Sfjoe 50289099Sfjoe buffer = sc->sc_rx_act ^ 1; 50389099Sfjoe 50489099Sfjoe /* Allocate header mbuf */ 505243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 50689099Sfjoe 50789099Sfjoe if (m == 0) { 50889099Sfjoe /* 50989099Sfjoe * in case s.th. goes wrong with mem, drop it 51089099Sfjoe * to make sure the receiver can be started again 51189099Sfjoe * count it as input error (we dont have any other 51289099Sfjoe * detectable) 51389099Sfjoe */ 51489099Sfjoe ifp->if_ierrors++; 51589099Sfjoe goto cleanup; 51689099Sfjoe } 51789099Sfjoe 51889099Sfjoe m->m_pkthdr.rcvif = ifp; 51989099Sfjoe 52089099Sfjoe /* 52189099Sfjoe * Align so that IP packet will be longword aligned. Here we 52289099Sfjoe * assume that m_data of new packet is longword aligned. 52389099Sfjoe * When implementing PHDS, we might have to change it to 2, 52489099Sfjoe * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent. 52589099Sfjoe */ 52689099Sfjoe 527109771Sfjoe cm_ram_ptr = buffer * 512; 52889099Sfjoe offset = GETMEM(cm_ram_ptr + 2); 52989099Sfjoe if (offset) 53089099Sfjoe len = 256 - offset; 53189099Sfjoe else { 53289099Sfjoe offset = GETMEM(cm_ram_ptr + 3); 53389099Sfjoe len = 512 - offset; 53489099Sfjoe } 53589099Sfjoe 53689099Sfjoe /* 53789099Sfjoe * first +2 bytes for align fixup below 53889099Sfjoe * second +2 bytes are for src/dst addresses 53989099Sfjoe */ 54089099Sfjoe if ((len + 2 + 2) > MHLEN) { 54189099Sfjoe /* attach an mbuf cluster */ 542243857Sglebius MCLGET(m, M_NOWAIT); 54389099Sfjoe 54489099Sfjoe /* Insist on getting a cluster */ 54589099Sfjoe if ((m->m_flags & M_EXT) == 0) { 54689099Sfjoe ifp->if_ierrors++; 54789099Sfjoe goto cleanup; 54889099Sfjoe } 54989099Sfjoe } 55089099Sfjoe 55189099Sfjoe if (m == 0) { 55289099Sfjoe ifp->if_ierrors++; 55389099Sfjoe goto cleanup; 55489099Sfjoe } 55589099Sfjoe 55689099Sfjoe type = GETMEM(cm_ram_ptr + offset); 55789099Sfjoe m->m_data += 1 + arc_isphds(type); 55889099Sfjoe /* mbuf filled with ARCnet addresses */ 55989099Sfjoe m->m_pkthdr.len = m->m_len = len + 2; 56089099Sfjoe 56189099Sfjoe ah = mtod(m, struct arc_header *); 56289099Sfjoe ah->arc_shost = GETMEM(cm_ram_ptr + 0); 56389099Sfjoe ah->arc_dhost = GETMEM(cm_ram_ptr + 1); 56489099Sfjoe 56589099Sfjoe bus_space_read_region_1( 56689099Sfjoe rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res), 56789099Sfjoe cm_ram_ptr + offset, mtod(m, u_char *) + 2, len); 56889099Sfjoe 569159529Sfjoe CM_UNLOCK(sc); 57089099Sfjoe arc_input(ifp, m); 571159529Sfjoe CM_LOCK(sc); 57289099Sfjoe 57389099Sfjoe m = NULL; 57489099Sfjoe ifp->if_ipackets++; 57589099Sfjoe 57689099Sfjoecleanup: 57789099Sfjoe 57889099Sfjoe if (m != NULL) 57989099Sfjoe m_freem(m); 58089099Sfjoe 58189099Sfjoe /* mark buffer as invalid by source id 0 */ 58289099Sfjoe PUTMEM(buffer << 9, 0); 58389099Sfjoe if (--sc->sc_rx_fillcount == 2 - 1) { 58489099Sfjoe 58589099Sfjoe /* was off, restart it on buffer just emptied */ 58689099Sfjoe sc->sc_rx_act = buffer; 58789099Sfjoe sc->sc_intmask |= CM_RI; 58889099Sfjoe 589172568Skevlo /* this also clears the RI flag interrupt: */ 59089099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 59189099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 59289099Sfjoe 59389099Sfjoe#ifdef CM_DEBUG 594104251Sbrooks if_printf(ifp, "srint: restarted rx on buf %d\n", buffer); 59589099Sfjoe#endif 59689099Sfjoe } 59789099Sfjoe} 59889099Sfjoe 599228471Sedstatic inline void 600159529Sfjoecm_tint_locked(sc, isr) 60189099Sfjoe struct cm_softc *sc; 60289099Sfjoe int isr; 60389099Sfjoe{ 60489099Sfjoe struct ifnet *ifp; 60589099Sfjoe 60689099Sfjoe int buffer; 60789099Sfjoe#ifdef CMTIMINGS 60889099Sfjoe int clknow; 60989099Sfjoe#endif 61089099Sfjoe 611147256Sbrooks ifp = sc->sc_ifp; 61289099Sfjoe buffer = sc->sc_tx_act; 61389099Sfjoe 61489099Sfjoe /* 61589099Sfjoe * retransmit code: 61689099Sfjoe * Normal situtations first for fast path: 61789099Sfjoe * If acknowledgement received ok or broadcast, we're ok. 61889099Sfjoe * else if 61989099Sfjoe */ 62089099Sfjoe 62189099Sfjoe if (isr & CM_TMA || sc->sc_broadcast[buffer]) 622147256Sbrooks ifp->if_opackets++; 62389099Sfjoe#ifdef CMRETRANSMIT 624199559Sjhb else if (ifp->if_flags & IFF_LINK2 && sc->sc_timer > 0 62589099Sfjoe && --sc->sc_retransmits[buffer] > 0) { 62689099Sfjoe /* retransmit same buffer */ 62789099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 62889099Sfjoe return; 62989099Sfjoe } 63089099Sfjoe#endif 63189099Sfjoe else 63289099Sfjoe ifp->if_oerrors++; 63389099Sfjoe 63489099Sfjoe 63589099Sfjoe /* We know we can accept another buffer at this point. */ 636148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 63789099Sfjoe 63889099Sfjoe if (--sc->sc_tx_fillcount > 0) { 63989099Sfjoe 64089099Sfjoe /* 64189099Sfjoe * start tx on other buffer. 64289099Sfjoe * This also clears the int flag 64389099Sfjoe */ 64489099Sfjoe buffer ^= 1; 64589099Sfjoe sc->sc_tx_act = buffer; 64689099Sfjoe 64789099Sfjoe /* 64889099Sfjoe * already given: 64989099Sfjoe * sc->sc_intmask |= CM_TA; 65089099Sfjoe * PUTREG(CMSTAT, sc->sc_intmask); 65189099Sfjoe */ 65289099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 65389099Sfjoe /* init watchdog timer */ 654199559Sjhb sc->sc_timer = ARCTIMEOUT; 65589099Sfjoe 65689099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 657104251Sbrooks if_printf(ifp, 658104251Sbrooks "tint: starting tx on buffer %d, status 0x%02x\n", 659104251Sbrooks buffer, GETREG(CMSTAT)); 66089099Sfjoe#endif 66189099Sfjoe } else { 66289099Sfjoe /* have to disable TX interrupt */ 66389099Sfjoe sc->sc_intmask &= ~CM_TA; 66489099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 66589099Sfjoe /* ... and watchdog timer */ 666199559Sjhb sc->sc_timer = 0; 66789099Sfjoe 66889099Sfjoe#ifdef CM_DEBUG 669104251Sbrooks if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n", 670104251Sbrooks GETREG(CMSTAT)); 67189099Sfjoe#endif 67289099Sfjoe } 67389099Sfjoe 67489099Sfjoe /* XXXX TODO */ 67589099Sfjoe#ifdef CMSOFTCOPY 67689099Sfjoe /* schedule soft int to fill a new buffer for us */ 67789099Sfjoe softintr_schedule(sc->sc_txcookie); 67889099Sfjoe#else 67989099Sfjoe /* call it directly */ 680159529Sfjoe cm_start_locked(ifp); 68189099Sfjoe#endif 68289099Sfjoe} 68389099Sfjoe 68489099Sfjoe/* 68589099Sfjoe * Our interrupt routine 68689099Sfjoe */ 68789099Sfjoevoid 68889099Sfjoecmintr(arg) 68989099Sfjoe void *arg; 69089099Sfjoe{ 69189099Sfjoe struct cm_softc *sc = arg; 692147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 69389099Sfjoe 69489099Sfjoe u_char isr, maskedisr; 69589099Sfjoe int buffer; 69689099Sfjoe u_long newsec; 69789099Sfjoe 698159529Sfjoe CM_LOCK(sc); 699159529Sfjoe 70089099Sfjoe isr = GETREG(CMSTAT); 70189099Sfjoe maskedisr = isr & sc->sc_intmask; 702159529Sfjoe if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 703159529Sfjoe CM_UNLOCK(sc); 70489099Sfjoe return; 705159529Sfjoe } 706159529Sfjoe 70789099Sfjoe do { 70889099Sfjoe 709109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 710104251Sbrooks if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n", 711104251Sbrooks isr, sc->sc_intmask); 71289099Sfjoe#endif 71389099Sfjoe 71489099Sfjoe if (maskedisr & CM_POR) { 71589099Sfjoe /* 71689099Sfjoe * XXX We should never see this. Don't bother to store 71789099Sfjoe * the address. 718147256Sbrooks * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF); 71989099Sfjoe */ 72089099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR)); 72189099Sfjoe log(LOG_WARNING, 722121816Sbrooks "%s: intr: got spurious power on reset int\n", 723121816Sbrooks ifp->if_xname); 72489099Sfjoe } 72589099Sfjoe 72689099Sfjoe if (maskedisr & CM_RECON) { 72789099Sfjoe /* 72889099Sfjoe * we dont need to: 72989099Sfjoe * PUTREG(CMCMD, CM_CONF(CONF_LONG)); 73089099Sfjoe */ 73189099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_RECONFIG)); 732147256Sbrooks ifp->if_collisions++; 73389099Sfjoe 73489099Sfjoe /* 73589099Sfjoe * If less than 2 seconds per reconfig: 73689099Sfjoe * If ARC_EXCESSIVE_RECONFIGS 73789099Sfjoe * since last burst, complain and set treshold for 73889099Sfjoe * warnings to ARC_EXCESSIVE_RECONS_REWARN. 73989099Sfjoe * 74089099Sfjoe * This allows for, e.g., new stations on the cable, or 74189099Sfjoe * cable switching as long as it is over after 74289099Sfjoe * (normally) 16 seconds. 74389099Sfjoe * 74489099Sfjoe * XXX TODO: check timeout bits in status word and 74589099Sfjoe * double time if necessary. 74689099Sfjoe */ 74789099Sfjoe 74889099Sfjoe callout_stop(&sc->sc_recon_ch); 74989099Sfjoe newsec = time_second; 75089099Sfjoe if ((newsec - sc->sc_recontime <= 2) && 75189099Sfjoe (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) { 75289099Sfjoe log(LOG_WARNING, 753121816Sbrooks "%s: excessive token losses, " 75489099Sfjoe "cable problem?\n", 755121816Sbrooks ifp->if_xname); 75689099Sfjoe } 75789099Sfjoe sc->sc_recontime = newsec; 75889099Sfjoe callout_reset(&sc->sc_recon_ch, 15 * hz, 759159529Sfjoe cm_reconwatch_locked, (void *)sc); 76089099Sfjoe } 76189099Sfjoe 76289099Sfjoe if (maskedisr & CM_RI) { 76389099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 764104251Sbrooks if_printf(ifp, "intr: hard rint, act %d\n", 765104251Sbrooks sc->sc_rx_act); 76689099Sfjoe#endif 76789099Sfjoe 76889099Sfjoe buffer = sc->sc_rx_act; 76989099Sfjoe /* look if buffer is marked invalid: */ 770109771Sfjoe if (GETMEM(buffer * 512) == 0) { 77189099Sfjoe /* 77289099Sfjoe * invalid marked buffer (or illegally 77389099Sfjoe * configured sender) 77489099Sfjoe */ 77589099Sfjoe log(LOG_WARNING, 776172568Skevlo "%s: spurious RX interrupt or sender 0 " 777121816Sbrooks " (ignored)\n", ifp->if_xname); 77889099Sfjoe /* 77989099Sfjoe * restart receiver on same buffer. 78089099Sfjoe * XXX maybe better reset interface? 78189099Sfjoe */ 78289099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 78389099Sfjoe } else { 78489099Sfjoe if (++sc->sc_rx_fillcount > 1) { 78589099Sfjoe sc->sc_intmask &= ~CM_RI; 78689099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 78789099Sfjoe } else { 78889099Sfjoe buffer ^= 1; 78989099Sfjoe sc->sc_rx_act = buffer; 79089099Sfjoe 79189099Sfjoe /* 79289099Sfjoe * Start receiver on other receive 79389099Sfjoe * buffer. This also clears the RI 794172568Skevlo * interrupt flag. 79589099Sfjoe */ 79689099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 79789099Sfjoe /* in RX intr, so mask is ok for RX */ 79889099Sfjoe 79989099Sfjoe#ifdef CM_DEBUG 800104251Sbrooks if_printf(ifp, "strt rx for buf %d, " 80189099Sfjoe "stat 0x%02x\n", 80289099Sfjoe sc->sc_rx_act, GETREG(CMSTAT)); 80389099Sfjoe#endif 80489099Sfjoe } 80589099Sfjoe 80689099Sfjoe#ifdef CMSOFTCOPY 80789099Sfjoe /* 80889099Sfjoe * this one starts a soft int to copy out 80989099Sfjoe * of the hw 81089099Sfjoe */ 81189099Sfjoe softintr_schedule(sc->sc_rxcookie); 81289099Sfjoe#else 81389099Sfjoe /* this one does the copy here */ 814159529Sfjoe cm_srint_locked(sc); 81589099Sfjoe#endif 81689099Sfjoe } 81789099Sfjoe } 81889099Sfjoe if (maskedisr & CM_TA) { 819159529Sfjoe cm_tint_locked(sc, isr); 82089099Sfjoe } 82189099Sfjoe isr = GETREG(CMSTAT); 82289099Sfjoe maskedisr = isr & sc->sc_intmask; 82389099Sfjoe } while (maskedisr); 824109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 825104251Sbrooks if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n", 826104251Sbrooks isr, sc->sc_intmask); 82789099Sfjoe#endif 828159529Sfjoe CM_UNLOCK(sc); 82989099Sfjoe} 83089099Sfjoe 83189099Sfjoevoid 832159529Sfjoecm_reconwatch_locked(arg) 83389099Sfjoe void *arg; 83489099Sfjoe{ 83589099Sfjoe struct cm_softc *sc = arg; 836147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 83789099Sfjoe 83889099Sfjoe if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) { 83989099Sfjoe sc->sc_reconcount = 0; 840121816Sbrooks log(LOG_WARNING, "%s: token valid again.\n", 841121816Sbrooks ifp->if_xname); 84289099Sfjoe } 84389099Sfjoe sc->sc_reconcount = 0; 84489099Sfjoe} 84589099Sfjoe 84689099Sfjoe 84789099Sfjoe/* 84889099Sfjoe * Process an ioctl request. 84989099Sfjoe * This code needs some work - it looks pretty ugly. 85089099Sfjoe */ 85189099Sfjoeint 85289099Sfjoecm_ioctl(ifp, command, data) 85389099Sfjoe struct ifnet *ifp; 85489099Sfjoe u_long command; 85589099Sfjoe caddr_t data; 85689099Sfjoe{ 85789099Sfjoe struct cm_softc *sc; 858159529Sfjoe int error; 85989099Sfjoe 86089099Sfjoe error = 0; 86189099Sfjoe sc = ifp->if_softc; 86289099Sfjoe 86389099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 2) 864104251Sbrooks if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command); 86589099Sfjoe#endif 86689099Sfjoe 86789099Sfjoe switch (command) { 86889099Sfjoe case SIOCSIFADDR: 869109771Sfjoe case SIOCGIFADDR: 87089099Sfjoe case SIOCADDMULTI: 87189099Sfjoe case SIOCDELMULTI: 87289099Sfjoe case SIOCSIFMTU: 87389099Sfjoe error = arc_ioctl(ifp, command, data); 87489099Sfjoe break; 87589099Sfjoe 87689099Sfjoe case SIOCSIFFLAGS: 877159529Sfjoe CM_LOCK(sc); 87889099Sfjoe if ((ifp->if_flags & IFF_UP) == 0 && 879148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 88089099Sfjoe /* 88189099Sfjoe * If interface is marked down and it is running, 88289099Sfjoe * then stop it. 88389099Sfjoe */ 884159529Sfjoe cm_stop_locked(sc); 885148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 88689099Sfjoe } else if ((ifp->if_flags & IFF_UP) != 0 && 887148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 88889099Sfjoe /* 88989099Sfjoe * If interface is marked up and it is stopped, then 89089099Sfjoe * start it. 89189099Sfjoe */ 892159529Sfjoe cm_init_locked(sc); 89389099Sfjoe } 894159529Sfjoe CM_UNLOCK(sc); 89589099Sfjoe break; 89689099Sfjoe 89789099Sfjoe default: 89889099Sfjoe error = EINVAL; 89989099Sfjoe break; 90089099Sfjoe } 90189099Sfjoe 90289099Sfjoe return (error); 90389099Sfjoe} 90489099Sfjoe 90589099Sfjoe/* 90689099Sfjoe * watchdog routine for transmitter. 90789099Sfjoe * 90889099Sfjoe * We need this, because else a receiver whose hardware is alive, but whose 90989099Sfjoe * software has not enabled the Receiver, would make our hardware wait forever 91089099Sfjoe * Discovered this after 20 times reading the docs. 91189099Sfjoe * 912108470Sschweikh * Only thing we do is disable transmitter. We'll get a transmit timeout, 91389099Sfjoe * and the int handler will have to decide not to retransmit (in case 91489099Sfjoe * retransmission is implemented). 91589099Sfjoe */ 91689099Sfjoevoid 917199559Sjhbcm_watchdog(void *arg) 91889099Sfjoe{ 919199559Sjhb struct cm_softc *sc; 92089099Sfjoe 921199559Sjhb sc = arg; 922199559Sjhb callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc); 923199559Sjhb if (sc->sc_timer == 0 || --sc->sc_timer > 0) 924199559Sjhb return; 92589099Sfjoe PUTREG(CMCMD, CM_TXDIS); 92689099Sfjoe} 927