189099Sfjoe/* $NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */ 289099Sfjoe 3119418Sobrien#include <sys/cdefs.h> 4119418Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/cm/smc90cx6.c 315221 2017-03-14 02:06:03Z pfg $"); 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> 59257176Sglebius#include <net/if_var.h> 6089099Sfjoe#include <net/if_dl.h> 6189099Sfjoe#include <net/if_types.h> 6289099Sfjoe#include <net/if_arc.h> 6389099Sfjoe 6489099Sfjoe#include <dev/cm/smc90cx6reg.h> 6589099Sfjoe#include <dev/cm/smc90cx6var.h> 6689099Sfjoe 6789099SfjoeMODULE_DEPEND(if_cm, arcnet, 1, 1, 1); 6889099Sfjoe 6989099Sfjoe/* these should be elsewhere */ 7089099Sfjoe 7189099Sfjoe#define ARC_MIN_LEN 1 7289099Sfjoe#define ARC_MIN_FORBID_LEN 254 7389099Sfjoe#define ARC_MAX_FORBID_LEN 256 7489099Sfjoe#define ARC_MAX_LEN 508 7589099Sfjoe#define ARC_ADDR_LEN 1 7689099Sfjoe 7789099Sfjoe/* for watchdog timer. This should be more than enough. */ 7889099Sfjoe#define ARCTIMEOUT (5*IFNET_SLOWHZ) 7989099Sfjoe 8089099Sfjoedevclass_t cm_devclass; 8189099Sfjoe 8289099Sfjoe/* 8389099Sfjoe * This currently uses 2 bufs for tx, 2 for rx 8489099Sfjoe * 8589099Sfjoe * New rx protocol: 8689099Sfjoe * 8789099Sfjoe * rx has a fillcount variable. If fillcount > (NRXBUF-1), 8889099Sfjoe * rx can be switched off from rx hard int. 8989099Sfjoe * Else rx is restarted on the other receiver. 9089099Sfjoe * rx soft int counts down. if it is == (NRXBUF-1), it restarts 9189099Sfjoe * the receiver. 9289099Sfjoe * To ensure packet ordering (we need that for 1201 later), we have a counter 9389099Sfjoe * which is incremented modulo 256 on each receive and a per buffer 9489099Sfjoe * variable, which is set to the counter on filling. The soft int can 9589099Sfjoe * compare both values to determine the older packet. 9689099Sfjoe * 9789099Sfjoe * Transmit direction: 9889099Sfjoe * 9989099Sfjoe * cm_start checks tx_fillcount 10089099Sfjoe * case 2: return 10189099Sfjoe * 10289099Sfjoe * else fill tx_act ^ 1 && inc tx_fillcount 10389099Sfjoe * 10489099Sfjoe * check tx_fillcount again. 105148887Srwatson * case 2: set IFF_DRV_OACTIVE to stop arc_output from filling us. 10689099Sfjoe * case 1: start tx 10789099Sfjoe * 108159529Sfjoe * tint clears IFF_OACTIVE, decrements and checks tx_fillcount 10989099Sfjoe * case 1: start tx on tx_act ^ 1, softcall cm_start 11089099Sfjoe * case 0: softcall cm_start 11189099Sfjoe * 11289099Sfjoe * #define fill(i) get mbuf && copy mbuf to chip(i) 11389099Sfjoe */ 11489099Sfjoe 11592739Salfredvoid cm_init(void *); 116159529Sfjoestatic void cm_init_locked(struct cm_softc *); 117159529Sfjoestatic void cm_reset_locked(struct cm_softc *); 11892739Salfredvoid cm_start(struct ifnet *); 119159529Sfjoevoid cm_start_locked(struct ifnet *); 12092739Salfredint cm_ioctl(struct ifnet *, unsigned long, caddr_t); 121199559Sjhbvoid cm_watchdog(void *); 122159529Sfjoevoid cm_srint_locked(void *vsc); 123159529Sfjoestatic void cm_tint_locked(struct cm_softc *, int); 124159529Sfjoevoid cm_reconwatch_locked(void *); 12589099Sfjoe 12689099Sfjoe/* 12789099Sfjoe * Release all resources 12889099Sfjoe */ 12989099Sfjoevoid 13089099Sfjoecm_release_resources(dev) 13189099Sfjoe device_t dev; 13289099Sfjoe{ 13389099Sfjoe struct cm_softc *sc = device_get_softc(dev); 13489099Sfjoe 135159529Sfjoe if (sc->port_res != NULL) { 13689099Sfjoe bus_release_resource(dev, SYS_RES_IOPORT, 137159529Sfjoe 0, sc->port_res); 138159529Sfjoe sc->port_res = NULL; 13989099Sfjoe } 140159529Sfjoe if (sc->mem_res != NULL) { 14189099Sfjoe bus_release_resource(dev, SYS_RES_MEMORY, 142159529Sfjoe 0, sc->mem_res); 143159529Sfjoe sc->mem_res = NULL; 14489099Sfjoe } 145159529Sfjoe if (sc->irq_res != NULL) { 14689099Sfjoe bus_release_resource(dev, SYS_RES_IRQ, 147159529Sfjoe 0, sc->irq_res); 148159529Sfjoe sc->irq_res = NULL; 14989099Sfjoe } 15089099Sfjoe} 15189099Sfjoe 15289099Sfjoeint 153121816Sbrookscm_attach(dev) 154121816Sbrooks device_t dev; 15589099Sfjoe{ 156121816Sbrooks struct cm_softc *sc = device_get_softc(dev); 157147256Sbrooks struct ifnet *ifp; 15889099Sfjoe u_int8_t linkaddress; 15989099Sfjoe 160147256Sbrooks ifp = sc->sc_ifp = if_alloc(IFT_ARCNET); 161159529Sfjoe if (ifp == NULL) 162147256Sbrooks return (ENOSPC); 163147256Sbrooks 16489099Sfjoe /* 16589099Sfjoe * read the arcnet address from the board 16689099Sfjoe */ 16789099Sfjoe GETREG(CMRESET); 16889099Sfjoe do { 16989099Sfjoe DELAY(200); 17089099Sfjoe } while (!(GETREG(CMSTAT) & CM_POR)); 17189099Sfjoe linkaddress = GETMEM(CMMACOFF); 17289099Sfjoe 17389099Sfjoe /* clear the int mask... */ 17489099Sfjoe sc->sc_intmask = 0; 17589099Sfjoe PUTREG(CMSTAT, 0); 17689099Sfjoe 17789099Sfjoe PUTREG(CMCMD, CM_CONF(CONF_LONG)); 17889099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); 17989099Sfjoe sc->sc_recontime = sc->sc_reconcount = 0; 18089099Sfjoe 18189099Sfjoe /* 18289099Sfjoe * set interface to stopped condition (reset) 18389099Sfjoe */ 184159529Sfjoe cm_stop_locked(sc); 18589099Sfjoe 186121752Sbrooks ifp->if_softc = sc; 187121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 188121752Sbrooks ifp->if_output = arc_output; 189121752Sbrooks ifp->if_start = cm_start; 190121752Sbrooks ifp->if_ioctl = cm_ioctl; 191121752Sbrooks ifp->if_init = cm_init; 192121752Sbrooks /* XXX IFQ_SET_READY(&ifp->if_snd); */ 193207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 194159529Sfjoe ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 19589099Sfjoe 196121752Sbrooks arc_ifattach(ifp, linkaddress); 19789099Sfjoe 19889099Sfjoe#ifdef CMSOFTCOPY 199121752Sbrooks sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc); 200121752Sbrooks sc->sc_txcookie = softintr_establish(IPL_SOFTNET, 201121752Sbrooks (void (*)(void *))cm_start, ifp); 20289099Sfjoe#endif 20389099Sfjoe 204159529Sfjoe callout_init_mtx(&sc->sc_recon_ch, &sc->sc_mtx, 0); 205199559Sjhb callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0); 20689099Sfjoe 207109771Sfjoe if_printf(ifp, "link addr 0x%02x (%d)\n", linkaddress, linkaddress); 20889099Sfjoe return 0; 20989099Sfjoe} 21089099Sfjoe 21189099Sfjoe/* 21289099Sfjoe * Initialize device 21389099Sfjoe * 21489099Sfjoe */ 21589099Sfjoevoid 21689099Sfjoecm_init(xsc) 21789099Sfjoe void *xsc; 21889099Sfjoe{ 21989099Sfjoe struct cm_softc *sc = (struct cm_softc *)xsc; 22089099Sfjoe 221159529Sfjoe CM_LOCK(sc); 222159529Sfjoe cm_init_locked(sc); 223159529Sfjoe CM_UNLOCK(sc); 224159529Sfjoe} 22589099Sfjoe 226159529Sfjoestatic void 227159529Sfjoecm_init_locked(struct cm_softc *sc) 228159529Sfjoe{ 229159529Sfjoe struct ifnet *ifp = sc->sc_ifp; 230159529Sfjoe 231148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 232148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 233159529Sfjoe cm_reset_locked(sc); 23489099Sfjoe } 23589099Sfjoe} 23689099Sfjoe 23789099Sfjoe/* 23889099Sfjoe * Reset the interface... 23989099Sfjoe * 240159529Sfjoe * Assumes that it is called with sc_mtx held 24189099Sfjoe */ 24289099Sfjoevoid 243159529Sfjoecm_reset_locked(sc) 24489099Sfjoe struct cm_softc *sc; 24589099Sfjoe{ 24689099Sfjoe struct ifnet *ifp; 24789099Sfjoe int linkaddress; 24889099Sfjoe 249147256Sbrooks ifp = sc->sc_ifp; 25089099Sfjoe 25189099Sfjoe#ifdef CM_DEBUG 252104251Sbrooks if_printf(ifp, "reset\n"); 25389099Sfjoe#endif 25489099Sfjoe /* stop and restart hardware */ 25589099Sfjoe 25689099Sfjoe GETREG(CMRESET); 25789099Sfjoe do { 25889099Sfjoe DELAY(200); 25989099Sfjoe } while (!(GETREG(CMSTAT) & CM_POR)); 26089099Sfjoe 26189099Sfjoe linkaddress = GETMEM(CMMACOFF); 26289099Sfjoe 26389099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 2) 264104251Sbrooks if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n", 265104251Sbrooks linkaddress, linkaddress); 26689099Sfjoe#endif 26789099Sfjoe 26889099Sfjoe /* tell the routing level about the (possibly changed) link address */ 26989099Sfjoe arc_storelladdr(ifp, linkaddress); 27089099Sfjoe arc_frag_init(ifp); 27189099Sfjoe 27289099Sfjoe /* POR is NMI, but we need it below: */ 27389099Sfjoe sc->sc_intmask = CM_RECON|CM_POR; 27489099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 27589099Sfjoe PUTREG(CMCMD, CM_CONF(CONF_LONG)); 27689099Sfjoe 27789099Sfjoe#ifdef CM_DEBUG 278104251Sbrooks if_printf(ifp, "reset: chip configured, status=0x%02x\n", 279104251Sbrooks GETREG(CMSTAT)); 28089099Sfjoe#endif 28189099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG)); 28289099Sfjoe 28389099Sfjoe#ifdef CM_DEBUG 284104251Sbrooks if_printf(ifp, "reset: bits cleared, status=0x%02x\n", 285104251Sbrooks GETREG(CMSTAT)); 28689099Sfjoe#endif 28789099Sfjoe 28889099Sfjoe sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS; 28989099Sfjoe 29089099Sfjoe /* start receiver */ 29189099Sfjoe 29289099Sfjoe sc->sc_intmask |= CM_RI; 29389099Sfjoe sc->sc_rx_fillcount = 0; 29489099Sfjoe sc->sc_rx_act = 2; 29589099Sfjoe 29689099Sfjoe PUTREG(CMCMD, CM_RXBC(2)); 29789099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 29889099Sfjoe 29989099Sfjoe#ifdef CM_DEBUG 300104251Sbrooks if_printf(ifp, "reset: started receiver, status=0x%02x\n", 301104251Sbrooks GETREG(CMSTAT)); 30289099Sfjoe#endif 30389099Sfjoe 30489099Sfjoe /* and init transmitter status */ 30589099Sfjoe sc->sc_tx_act = 0; 30689099Sfjoe sc->sc_tx_fillcount = 0; 30789099Sfjoe 308148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 309148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 31089099Sfjoe 311199559Sjhb callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc); 312159529Sfjoe cm_start_locked(ifp); 31389099Sfjoe} 31489099Sfjoe 31589099Sfjoe/* 31689099Sfjoe * Take interface offline 31789099Sfjoe */ 31889099Sfjoevoid 319159529Sfjoecm_stop_locked(sc) 32089099Sfjoe struct cm_softc *sc; 32189099Sfjoe{ 32289099Sfjoe /* Stop the interrupts */ 32389099Sfjoe PUTREG(CMSTAT, 0); 32489099Sfjoe 32589099Sfjoe /* Stop the interface */ 32689099Sfjoe GETREG(CMRESET); 32789099Sfjoe 32889099Sfjoe /* Stop watchdog timer */ 329199559Sjhb callout_stop(&sc->sc_watchdog_timer); 330199559Sjhb sc->sc_timer = 0; 33189099Sfjoe} 33289099Sfjoe 333159529Sfjoevoid 334159529Sfjoecm_start(struct ifnet *ifp) 335159529Sfjoe{ 336159529Sfjoe struct cm_softc *sc = ifp->if_softc; 337159529Sfjoe 338159529Sfjoe CM_LOCK(sc); 339159529Sfjoe cm_start_locked(ifp); 340159529Sfjoe CM_UNLOCK(sc); 341159529Sfjoe} 342159529Sfjoe 34389099Sfjoe/* 34489099Sfjoe * Start output on interface. Get another datagram to send 34589099Sfjoe * off the interface queue, and copy it to the 34689099Sfjoe * interface becore starting the output 34789099Sfjoe * 348159529Sfjoe * Assumes that sc_mtx is held 34989099Sfjoe */ 35089099Sfjoevoid 351159529Sfjoecm_start_locked(ifp) 35289099Sfjoe struct ifnet *ifp; 35389099Sfjoe{ 35489099Sfjoe struct cm_softc *sc = ifp->if_softc; 355159529Sfjoe struct mbuf *m, *mp; 35689099Sfjoe 35789099Sfjoe int cm_ram_ptr; 358159529Sfjoe int len, tlen, offset, buffer; 35989099Sfjoe#ifdef CMTIMINGS 36089099Sfjoe u_long copystart, lencopy, perbyte; 36189099Sfjoe#endif 36289099Sfjoe 36389099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 3) 364104251Sbrooks if_printf(ifp, "start(%p)\n", ifp); 36589099Sfjoe#endif 36689099Sfjoe 367148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 36889099Sfjoe return; 36989099Sfjoe 370159529Sfjoe if (sc->sc_tx_fillcount >= 2) 37189099Sfjoe return; 37289099Sfjoe 37389099Sfjoe m = arc_frag_next(ifp); 37489099Sfjoe buffer = sc->sc_tx_act ^ 1; 37589099Sfjoe 376315221Spfg if (m == NULL) 37789099Sfjoe return; 37889099Sfjoe 37989099Sfjoe#ifdef CM_DEBUG 38089099Sfjoe if (m->m_len < ARC_HDRLEN) 38189099Sfjoe m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */ 382104251Sbrooks if_printf(ifp, "start: filling %d from %d to %d type %d\n", 383104251Sbrooks buffer, mtod(m, u_char *)[0], 38489099Sfjoe mtod(m, u_char *)[1], mtod(m, u_char *)[2]); 38589099Sfjoe#else 38689099Sfjoe if (m->m_len < 2) 38789099Sfjoe m = m_pullup(m, 2); 38889099Sfjoe#endif 389109771Sfjoe cm_ram_ptr = buffer * 512; 39089099Sfjoe 391315221Spfg if (m == NULL) 39289099Sfjoe return; 39389099Sfjoe 39489099Sfjoe /* write the addresses to RAM and throw them away */ 39589099Sfjoe 39689099Sfjoe /* 39789099Sfjoe * Hardware does this: Yet Another Microsecond Saved. 39889099Sfjoe * (btw, timing code says usually 2 microseconds) 39989099Sfjoe * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]); 40089099Sfjoe */ 40189099Sfjoe 40289099Sfjoe PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]); 40389099Sfjoe m_adj(m, 2); 40489099Sfjoe 40589099Sfjoe /* get total length left at this point */ 40689099Sfjoe tlen = m->m_pkthdr.len; 40789099Sfjoe if (tlen < ARC_MIN_FORBID_LEN) { 40889099Sfjoe offset = 256 - tlen; 40989099Sfjoe PUTMEM(cm_ram_ptr + 2, offset); 41089099Sfjoe } else { 41189099Sfjoe PUTMEM(cm_ram_ptr + 2, 0); 41289099Sfjoe if (tlen <= ARC_MAX_FORBID_LEN) 41389099Sfjoe offset = 255; /* !!! */ 41489099Sfjoe else { 41589099Sfjoe if (tlen > ARC_MAX_LEN) 41689099Sfjoe tlen = ARC_MAX_LEN; 41789099Sfjoe offset = 512 - tlen; 41889099Sfjoe } 41989099Sfjoe PUTMEM(cm_ram_ptr + 3, offset); 42089099Sfjoe 42189099Sfjoe } 42289099Sfjoe cm_ram_ptr += offset; 42389099Sfjoe 42489099Sfjoe /* lets loop through the mbuf chain */ 42589099Sfjoe 42689099Sfjoe for (mp = m; mp; mp = mp->m_next) { 42789099Sfjoe if ((len = mp->m_len)) { /* YAMS */ 42889099Sfjoe bus_space_write_region_1( 42989099Sfjoe rman_get_bustag(sc->mem_res), 43089099Sfjoe rman_get_bushandle(sc->mem_res), 43189099Sfjoe cm_ram_ptr, mtod(mp, caddr_t), len); 43289099Sfjoe 43389099Sfjoe cm_ram_ptr += len; 43489099Sfjoe } 43589099Sfjoe } 43689099Sfjoe 43789099Sfjoe sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0; 43889099Sfjoe sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5; 43989099Sfjoe 44089099Sfjoe if (++sc->sc_tx_fillcount > 1) { 44189099Sfjoe /* 44289099Sfjoe * We are filled up to the rim. No more bufs for the moment, 44389099Sfjoe * please. 44489099Sfjoe */ 445148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 44689099Sfjoe } else { 44789099Sfjoe#ifdef CM_DEBUG 448104251Sbrooks if_printf(ifp, "start: starting transmitter on buffer %d\n", 449104251Sbrooks buffer); 45089099Sfjoe#endif 45189099Sfjoe /* Transmitter was off, start it */ 45289099Sfjoe sc->sc_tx_act = buffer; 45389099Sfjoe 45489099Sfjoe /* 45589099Sfjoe * We still can accept another buf, so don't: 456148887Srwatson * ifp->if_drv_flags |= IFF_DRV_OACTIVE; 45789099Sfjoe */ 45889099Sfjoe sc->sc_intmask |= CM_TA; 45989099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 46089099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 46189099Sfjoe 462199559Sjhb sc->sc_timer = ARCTIMEOUT; 46389099Sfjoe } 46489099Sfjoe m_freem(m); 46589099Sfjoe 46689099Sfjoe /* 46789099Sfjoe * After 10 times reading the docs, I realized 46889099Sfjoe * that in the case the receiver NAKs the buffer request, 46989099Sfjoe * the hardware retries till shutdown. 47089099Sfjoe * This is integrated now in the code above. 47189099Sfjoe */ 472159529Sfjoe} 47389099Sfjoe 474159529Sfjoe#ifdef CMSOFTCOPY 475159529Sfjoevoid 476159529Sfjoecm_srint(void *vsc) 477159529Sfjoe{ 478159529Sfjoe struct cm_softc *sc = (struct cm_softc *)vsc; 479159529Sfjoe 480159529Sfjoe CM_LOCK(sc); 481159529Sfjoe cm_srint_locked(vsc); 482159529Sfjoe CM_UNLOCK(sc); 48389099Sfjoe} 484159529Sfjoe#endif 48589099Sfjoe 48689099Sfjoe/* 48789099Sfjoe * Arcnet interface receiver soft interrupt: 48889099Sfjoe * get the stuff out of any filled buffer we find. 48989099Sfjoe */ 49089099Sfjoevoid 491159529Sfjoecm_srint_locked(vsc) 49289099Sfjoe void *vsc; 49389099Sfjoe{ 49489099Sfjoe struct cm_softc *sc = (struct cm_softc *)vsc; 495159529Sfjoe int buffer, len, offset, type; 49689099Sfjoe int cm_ram_ptr; 49789099Sfjoe struct mbuf *m; 49889099Sfjoe struct arc_header *ah; 49989099Sfjoe struct ifnet *ifp; 50089099Sfjoe 501147256Sbrooks ifp = sc->sc_ifp; 50289099Sfjoe 50389099Sfjoe buffer = sc->sc_rx_act ^ 1; 50489099Sfjoe 50589099Sfjoe /* Allocate header mbuf */ 506243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 50789099Sfjoe 508315221Spfg if (m == NULL) { 50989099Sfjoe /* 51089099Sfjoe * in case s.th. goes wrong with mem, drop it 51189099Sfjoe * to make sure the receiver can be started again 51289099Sfjoe * count it as input error (we dont have any other 51389099Sfjoe * detectable) 51489099Sfjoe */ 515271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 51689099Sfjoe goto cleanup; 51789099Sfjoe } 51889099Sfjoe 51989099Sfjoe m->m_pkthdr.rcvif = ifp; 52089099Sfjoe 52189099Sfjoe /* 52289099Sfjoe * Align so that IP packet will be longword aligned. Here we 52389099Sfjoe * assume that m_data of new packet is longword aligned. 52489099Sfjoe * When implementing PHDS, we might have to change it to 2, 52589099Sfjoe * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent. 52689099Sfjoe */ 52789099Sfjoe 528109771Sfjoe cm_ram_ptr = buffer * 512; 52989099Sfjoe offset = GETMEM(cm_ram_ptr + 2); 53089099Sfjoe if (offset) 53189099Sfjoe len = 256 - offset; 53289099Sfjoe else { 53389099Sfjoe offset = GETMEM(cm_ram_ptr + 3); 53489099Sfjoe len = 512 - offset; 53589099Sfjoe } 53689099Sfjoe 53789099Sfjoe /* 53889099Sfjoe * first +2 bytes for align fixup below 53989099Sfjoe * second +2 bytes are for src/dst addresses 54089099Sfjoe */ 54189099Sfjoe if ((len + 2 + 2) > MHLEN) { 54289099Sfjoe /* attach an mbuf cluster */ 543276750Srwatson if (!(MCLGET(m, M_NOWAIT))) { 544271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 54589099Sfjoe goto cleanup; 54689099Sfjoe } 54789099Sfjoe } 54889099Sfjoe 549315221Spfg if (m == NULL) { 550271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 55189099Sfjoe goto cleanup; 55289099Sfjoe } 55389099Sfjoe 55489099Sfjoe type = GETMEM(cm_ram_ptr + offset); 55589099Sfjoe m->m_data += 1 + arc_isphds(type); 55689099Sfjoe /* mbuf filled with ARCnet addresses */ 55789099Sfjoe m->m_pkthdr.len = m->m_len = len + 2; 55889099Sfjoe 55989099Sfjoe ah = mtod(m, struct arc_header *); 56089099Sfjoe ah->arc_shost = GETMEM(cm_ram_ptr + 0); 56189099Sfjoe ah->arc_dhost = GETMEM(cm_ram_ptr + 1); 56289099Sfjoe 56389099Sfjoe bus_space_read_region_1( 56489099Sfjoe rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res), 56589099Sfjoe cm_ram_ptr + offset, mtod(m, u_char *) + 2, len); 56689099Sfjoe 567159529Sfjoe CM_UNLOCK(sc); 56889099Sfjoe arc_input(ifp, m); 569159529Sfjoe CM_LOCK(sc); 57089099Sfjoe 57189099Sfjoe m = NULL; 572271849Sglebius if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 57389099Sfjoe 57489099Sfjoecleanup: 57589099Sfjoe 57689099Sfjoe if (m != NULL) 57789099Sfjoe m_freem(m); 57889099Sfjoe 57989099Sfjoe /* mark buffer as invalid by source id 0 */ 58089099Sfjoe PUTMEM(buffer << 9, 0); 58189099Sfjoe if (--sc->sc_rx_fillcount == 2 - 1) { 58289099Sfjoe 58389099Sfjoe /* was off, restart it on buffer just emptied */ 58489099Sfjoe sc->sc_rx_act = buffer; 58589099Sfjoe sc->sc_intmask |= CM_RI; 58689099Sfjoe 587172568Skevlo /* this also clears the RI flag interrupt: */ 58889099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 58989099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 59089099Sfjoe 59189099Sfjoe#ifdef CM_DEBUG 592104251Sbrooks if_printf(ifp, "srint: restarted rx on buf %d\n", buffer); 59389099Sfjoe#endif 59489099Sfjoe } 59589099Sfjoe} 59689099Sfjoe 597228471Sedstatic inline void 598159529Sfjoecm_tint_locked(sc, isr) 59989099Sfjoe struct cm_softc *sc; 60089099Sfjoe int isr; 60189099Sfjoe{ 60289099Sfjoe struct ifnet *ifp; 60389099Sfjoe 60489099Sfjoe int buffer; 60589099Sfjoe#ifdef CMTIMINGS 60689099Sfjoe int clknow; 60789099Sfjoe#endif 60889099Sfjoe 609147256Sbrooks ifp = sc->sc_ifp; 61089099Sfjoe buffer = sc->sc_tx_act; 61189099Sfjoe 61289099Sfjoe /* 61389099Sfjoe * retransmit code: 61489099Sfjoe * Normal situtations first for fast path: 61589099Sfjoe * If acknowledgement received ok or broadcast, we're ok. 61689099Sfjoe * else if 61789099Sfjoe */ 61889099Sfjoe 61989099Sfjoe if (isr & CM_TMA || sc->sc_broadcast[buffer]) 620271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 62189099Sfjoe#ifdef CMRETRANSMIT 622199559Sjhb else if (ifp->if_flags & IFF_LINK2 && sc->sc_timer > 0 62389099Sfjoe && --sc->sc_retransmits[buffer] > 0) { 62489099Sfjoe /* retransmit same buffer */ 62589099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 62689099Sfjoe return; 62789099Sfjoe } 62889099Sfjoe#endif 62989099Sfjoe else 630271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 63189099Sfjoe 63289099Sfjoe 63389099Sfjoe /* We know we can accept another buffer at this point. */ 634148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 63589099Sfjoe 63689099Sfjoe if (--sc->sc_tx_fillcount > 0) { 63789099Sfjoe 63889099Sfjoe /* 63989099Sfjoe * start tx on other buffer. 64089099Sfjoe * This also clears the int flag 64189099Sfjoe */ 64289099Sfjoe buffer ^= 1; 64389099Sfjoe sc->sc_tx_act = buffer; 64489099Sfjoe 64589099Sfjoe /* 64689099Sfjoe * already given: 64789099Sfjoe * sc->sc_intmask |= CM_TA; 64889099Sfjoe * PUTREG(CMSTAT, sc->sc_intmask); 64989099Sfjoe */ 65089099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 65189099Sfjoe /* init watchdog timer */ 652199559Sjhb sc->sc_timer = ARCTIMEOUT; 65389099Sfjoe 65489099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 655104251Sbrooks if_printf(ifp, 656104251Sbrooks "tint: starting tx on buffer %d, status 0x%02x\n", 657104251Sbrooks buffer, GETREG(CMSTAT)); 65889099Sfjoe#endif 65989099Sfjoe } else { 66089099Sfjoe /* have to disable TX interrupt */ 66189099Sfjoe sc->sc_intmask &= ~CM_TA; 66289099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 66389099Sfjoe /* ... and watchdog timer */ 664199559Sjhb sc->sc_timer = 0; 66589099Sfjoe 66689099Sfjoe#ifdef CM_DEBUG 667104251Sbrooks if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n", 668104251Sbrooks GETREG(CMSTAT)); 66989099Sfjoe#endif 67089099Sfjoe } 67189099Sfjoe 67289099Sfjoe /* XXXX TODO */ 67389099Sfjoe#ifdef CMSOFTCOPY 67489099Sfjoe /* schedule soft int to fill a new buffer for us */ 67589099Sfjoe softintr_schedule(sc->sc_txcookie); 67689099Sfjoe#else 67789099Sfjoe /* call it directly */ 678159529Sfjoe cm_start_locked(ifp); 67989099Sfjoe#endif 68089099Sfjoe} 68189099Sfjoe 68289099Sfjoe/* 68389099Sfjoe * Our interrupt routine 68489099Sfjoe */ 68589099Sfjoevoid 68689099Sfjoecmintr(arg) 68789099Sfjoe void *arg; 68889099Sfjoe{ 68989099Sfjoe struct cm_softc *sc = arg; 690147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 69189099Sfjoe 69289099Sfjoe u_char isr, maskedisr; 69389099Sfjoe int buffer; 69489099Sfjoe u_long newsec; 69589099Sfjoe 696159529Sfjoe CM_LOCK(sc); 697159529Sfjoe 69889099Sfjoe isr = GETREG(CMSTAT); 69989099Sfjoe maskedisr = isr & sc->sc_intmask; 700159529Sfjoe if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 701159529Sfjoe CM_UNLOCK(sc); 70289099Sfjoe return; 703159529Sfjoe } 704159529Sfjoe 70589099Sfjoe do { 70689099Sfjoe 707109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 708104251Sbrooks if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n", 709104251Sbrooks isr, sc->sc_intmask); 71089099Sfjoe#endif 71189099Sfjoe 71289099Sfjoe if (maskedisr & CM_POR) { 71389099Sfjoe /* 71489099Sfjoe * XXX We should never see this. Don't bother to store 71589099Sfjoe * the address. 716147256Sbrooks * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF); 71789099Sfjoe */ 71889099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR)); 71989099Sfjoe log(LOG_WARNING, 720121816Sbrooks "%s: intr: got spurious power on reset int\n", 721121816Sbrooks ifp->if_xname); 72289099Sfjoe } 72389099Sfjoe 72489099Sfjoe if (maskedisr & CM_RECON) { 72589099Sfjoe /* 72689099Sfjoe * we dont need to: 72789099Sfjoe * PUTREG(CMCMD, CM_CONF(CONF_LONG)); 72889099Sfjoe */ 72989099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_RECONFIG)); 730271849Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); 73189099Sfjoe 73289099Sfjoe /* 73389099Sfjoe * If less than 2 seconds per reconfig: 73489099Sfjoe * If ARC_EXCESSIVE_RECONFIGS 73589099Sfjoe * since last burst, complain and set treshold for 73689099Sfjoe * warnings to ARC_EXCESSIVE_RECONS_REWARN. 73789099Sfjoe * 73889099Sfjoe * This allows for, e.g., new stations on the cable, or 73989099Sfjoe * cable switching as long as it is over after 74089099Sfjoe * (normally) 16 seconds. 74189099Sfjoe * 74289099Sfjoe * XXX TODO: check timeout bits in status word and 74389099Sfjoe * double time if necessary. 74489099Sfjoe */ 74589099Sfjoe 74689099Sfjoe callout_stop(&sc->sc_recon_ch); 74789099Sfjoe newsec = time_second; 74889099Sfjoe if ((newsec - sc->sc_recontime <= 2) && 74989099Sfjoe (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) { 75089099Sfjoe log(LOG_WARNING, 751121816Sbrooks "%s: excessive token losses, " 75289099Sfjoe "cable problem?\n", 753121816Sbrooks ifp->if_xname); 75489099Sfjoe } 75589099Sfjoe sc->sc_recontime = newsec; 75689099Sfjoe callout_reset(&sc->sc_recon_ch, 15 * hz, 757159529Sfjoe cm_reconwatch_locked, (void *)sc); 75889099Sfjoe } 75989099Sfjoe 76089099Sfjoe if (maskedisr & CM_RI) { 76189099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 762104251Sbrooks if_printf(ifp, "intr: hard rint, act %d\n", 763104251Sbrooks sc->sc_rx_act); 76489099Sfjoe#endif 76589099Sfjoe 76689099Sfjoe buffer = sc->sc_rx_act; 76789099Sfjoe /* look if buffer is marked invalid: */ 768109771Sfjoe if (GETMEM(buffer * 512) == 0) { 76989099Sfjoe /* 77089099Sfjoe * invalid marked buffer (or illegally 77189099Sfjoe * configured sender) 77289099Sfjoe */ 77389099Sfjoe log(LOG_WARNING, 774172568Skevlo "%s: spurious RX interrupt or sender 0 " 775121816Sbrooks " (ignored)\n", ifp->if_xname); 77689099Sfjoe /* 77789099Sfjoe * restart receiver on same buffer. 77889099Sfjoe * XXX maybe better reset interface? 77989099Sfjoe */ 78089099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 78189099Sfjoe } else { 78289099Sfjoe if (++sc->sc_rx_fillcount > 1) { 78389099Sfjoe sc->sc_intmask &= ~CM_RI; 78489099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 78589099Sfjoe } else { 78689099Sfjoe buffer ^= 1; 78789099Sfjoe sc->sc_rx_act = buffer; 78889099Sfjoe 78989099Sfjoe /* 79089099Sfjoe * Start receiver on other receive 79189099Sfjoe * buffer. This also clears the RI 792172568Skevlo * interrupt flag. 79389099Sfjoe */ 79489099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 79589099Sfjoe /* in RX intr, so mask is ok for RX */ 79689099Sfjoe 79789099Sfjoe#ifdef CM_DEBUG 798104251Sbrooks if_printf(ifp, "strt rx for buf %d, " 79989099Sfjoe "stat 0x%02x\n", 80089099Sfjoe sc->sc_rx_act, GETREG(CMSTAT)); 80189099Sfjoe#endif 80289099Sfjoe } 80389099Sfjoe 80489099Sfjoe#ifdef CMSOFTCOPY 80589099Sfjoe /* 80689099Sfjoe * this one starts a soft int to copy out 80789099Sfjoe * of the hw 80889099Sfjoe */ 80989099Sfjoe softintr_schedule(sc->sc_rxcookie); 81089099Sfjoe#else 81189099Sfjoe /* this one does the copy here */ 812159529Sfjoe cm_srint_locked(sc); 81389099Sfjoe#endif 81489099Sfjoe } 81589099Sfjoe } 81689099Sfjoe if (maskedisr & CM_TA) { 817159529Sfjoe cm_tint_locked(sc, isr); 81889099Sfjoe } 81989099Sfjoe isr = GETREG(CMSTAT); 82089099Sfjoe maskedisr = isr & sc->sc_intmask; 82189099Sfjoe } while (maskedisr); 822109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 823104251Sbrooks if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n", 824104251Sbrooks isr, sc->sc_intmask); 82589099Sfjoe#endif 826159529Sfjoe CM_UNLOCK(sc); 82789099Sfjoe} 82889099Sfjoe 82989099Sfjoevoid 830159529Sfjoecm_reconwatch_locked(arg) 83189099Sfjoe void *arg; 83289099Sfjoe{ 83389099Sfjoe struct cm_softc *sc = arg; 834147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 83589099Sfjoe 83689099Sfjoe if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) { 83789099Sfjoe sc->sc_reconcount = 0; 838121816Sbrooks log(LOG_WARNING, "%s: token valid again.\n", 839121816Sbrooks ifp->if_xname); 84089099Sfjoe } 84189099Sfjoe sc->sc_reconcount = 0; 84289099Sfjoe} 84389099Sfjoe 84489099Sfjoe 84589099Sfjoe/* 84689099Sfjoe * Process an ioctl request. 84789099Sfjoe * This code needs some work - it looks pretty ugly. 84889099Sfjoe */ 84989099Sfjoeint 85089099Sfjoecm_ioctl(ifp, command, data) 85189099Sfjoe struct ifnet *ifp; 85289099Sfjoe u_long command; 85389099Sfjoe caddr_t data; 85489099Sfjoe{ 85589099Sfjoe struct cm_softc *sc; 856159529Sfjoe int error; 85789099Sfjoe 85889099Sfjoe error = 0; 85989099Sfjoe sc = ifp->if_softc; 86089099Sfjoe 86189099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 2) 862104251Sbrooks if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command); 86389099Sfjoe#endif 86489099Sfjoe 86589099Sfjoe switch (command) { 86689099Sfjoe case SIOCSIFADDR: 867109771Sfjoe case SIOCGIFADDR: 86889099Sfjoe case SIOCADDMULTI: 86989099Sfjoe case SIOCDELMULTI: 87089099Sfjoe case SIOCSIFMTU: 87189099Sfjoe error = arc_ioctl(ifp, command, data); 87289099Sfjoe break; 87389099Sfjoe 87489099Sfjoe case SIOCSIFFLAGS: 875159529Sfjoe CM_LOCK(sc); 87689099Sfjoe if ((ifp->if_flags & IFF_UP) == 0 && 877148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 87889099Sfjoe /* 87989099Sfjoe * If interface is marked down and it is running, 88089099Sfjoe * then stop it. 88189099Sfjoe */ 882159529Sfjoe cm_stop_locked(sc); 883148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 88489099Sfjoe } else if ((ifp->if_flags & IFF_UP) != 0 && 885148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 88689099Sfjoe /* 88789099Sfjoe * If interface is marked up and it is stopped, then 88889099Sfjoe * start it. 88989099Sfjoe */ 890159529Sfjoe cm_init_locked(sc); 89189099Sfjoe } 892159529Sfjoe CM_UNLOCK(sc); 89389099Sfjoe break; 89489099Sfjoe 89589099Sfjoe default: 89689099Sfjoe error = EINVAL; 89789099Sfjoe break; 89889099Sfjoe } 89989099Sfjoe 90089099Sfjoe return (error); 90189099Sfjoe} 90289099Sfjoe 90389099Sfjoe/* 90489099Sfjoe * watchdog routine for transmitter. 90589099Sfjoe * 90689099Sfjoe * We need this, because else a receiver whose hardware is alive, but whose 90789099Sfjoe * software has not enabled the Receiver, would make our hardware wait forever 90889099Sfjoe * Discovered this after 20 times reading the docs. 90989099Sfjoe * 910108470Sschweikh * Only thing we do is disable transmitter. We'll get a transmit timeout, 91189099Sfjoe * and the int handler will have to decide not to retransmit (in case 91289099Sfjoe * retransmission is implemented). 91389099Sfjoe */ 91489099Sfjoevoid 915199559Sjhbcm_watchdog(void *arg) 91689099Sfjoe{ 917199559Sjhb struct cm_softc *sc; 91889099Sfjoe 919199559Sjhb sc = arg; 920199559Sjhb callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc); 921199559Sjhb if (sc->sc_timer == 0 || --sc->sc_timer > 0) 922199559Sjhb return; 92389099Sfjoe PUTREG(CMCMD, CM_TXDIS); 92489099Sfjoe} 925