smc90cx6.c revision 271849
189099Sfjoe/* $NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */ 289099Sfjoe 3119418Sobrien#include <sys/cdefs.h> 4119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/cm/smc90cx6.c 271849 2014-09-19 03:51:26Z glebius $"); 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 37689099Sfjoe if (m == 0) 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 39189099Sfjoe if (m == 0) 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 50889099Sfjoe if (m == 0) { 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 */ 543243857Sglebius MCLGET(m, M_NOWAIT); 54489099Sfjoe 54589099Sfjoe /* Insist on getting a cluster */ 54689099Sfjoe if ((m->m_flags & M_EXT) == 0) { 547271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 54889099Sfjoe goto cleanup; 54989099Sfjoe } 55089099Sfjoe } 55189099Sfjoe 55289099Sfjoe if (m == 0) { 553271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 55489099Sfjoe goto cleanup; 55589099Sfjoe } 55689099Sfjoe 55789099Sfjoe type = GETMEM(cm_ram_ptr + offset); 55889099Sfjoe m->m_data += 1 + arc_isphds(type); 55989099Sfjoe /* mbuf filled with ARCnet addresses */ 56089099Sfjoe m->m_pkthdr.len = m->m_len = len + 2; 56189099Sfjoe 56289099Sfjoe ah = mtod(m, struct arc_header *); 56389099Sfjoe ah->arc_shost = GETMEM(cm_ram_ptr + 0); 56489099Sfjoe ah->arc_dhost = GETMEM(cm_ram_ptr + 1); 56589099Sfjoe 56689099Sfjoe bus_space_read_region_1( 56789099Sfjoe rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res), 56889099Sfjoe cm_ram_ptr + offset, mtod(m, u_char *) + 2, len); 56989099Sfjoe 570159529Sfjoe CM_UNLOCK(sc); 57189099Sfjoe arc_input(ifp, m); 572159529Sfjoe CM_LOCK(sc); 57389099Sfjoe 57489099Sfjoe m = NULL; 575271849Sglebius if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 57689099Sfjoe 57789099Sfjoecleanup: 57889099Sfjoe 57989099Sfjoe if (m != NULL) 58089099Sfjoe m_freem(m); 58189099Sfjoe 58289099Sfjoe /* mark buffer as invalid by source id 0 */ 58389099Sfjoe PUTMEM(buffer << 9, 0); 58489099Sfjoe if (--sc->sc_rx_fillcount == 2 - 1) { 58589099Sfjoe 58689099Sfjoe /* was off, restart it on buffer just emptied */ 58789099Sfjoe sc->sc_rx_act = buffer; 58889099Sfjoe sc->sc_intmask |= CM_RI; 58989099Sfjoe 590172568Skevlo /* this also clears the RI flag interrupt: */ 59189099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 59289099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 59389099Sfjoe 59489099Sfjoe#ifdef CM_DEBUG 595104251Sbrooks if_printf(ifp, "srint: restarted rx on buf %d\n", buffer); 59689099Sfjoe#endif 59789099Sfjoe } 59889099Sfjoe} 59989099Sfjoe 600228471Sedstatic inline void 601159529Sfjoecm_tint_locked(sc, isr) 60289099Sfjoe struct cm_softc *sc; 60389099Sfjoe int isr; 60489099Sfjoe{ 60589099Sfjoe struct ifnet *ifp; 60689099Sfjoe 60789099Sfjoe int buffer; 60889099Sfjoe#ifdef CMTIMINGS 60989099Sfjoe int clknow; 61089099Sfjoe#endif 61189099Sfjoe 612147256Sbrooks ifp = sc->sc_ifp; 61389099Sfjoe buffer = sc->sc_tx_act; 61489099Sfjoe 61589099Sfjoe /* 61689099Sfjoe * retransmit code: 61789099Sfjoe * Normal situtations first for fast path: 61889099Sfjoe * If acknowledgement received ok or broadcast, we're ok. 61989099Sfjoe * else if 62089099Sfjoe */ 62189099Sfjoe 62289099Sfjoe if (isr & CM_TMA || sc->sc_broadcast[buffer]) 623271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 62489099Sfjoe#ifdef CMRETRANSMIT 625199559Sjhb else if (ifp->if_flags & IFF_LINK2 && sc->sc_timer > 0 62689099Sfjoe && --sc->sc_retransmits[buffer] > 0) { 62789099Sfjoe /* retransmit same buffer */ 62889099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 62989099Sfjoe return; 63089099Sfjoe } 63189099Sfjoe#endif 63289099Sfjoe else 633271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 63489099Sfjoe 63589099Sfjoe 63689099Sfjoe /* We know we can accept another buffer at this point. */ 637148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 63889099Sfjoe 63989099Sfjoe if (--sc->sc_tx_fillcount > 0) { 64089099Sfjoe 64189099Sfjoe /* 64289099Sfjoe * start tx on other buffer. 64389099Sfjoe * This also clears the int flag 64489099Sfjoe */ 64589099Sfjoe buffer ^= 1; 64689099Sfjoe sc->sc_tx_act = buffer; 64789099Sfjoe 64889099Sfjoe /* 64989099Sfjoe * already given: 65089099Sfjoe * sc->sc_intmask |= CM_TA; 65189099Sfjoe * PUTREG(CMSTAT, sc->sc_intmask); 65289099Sfjoe */ 65389099Sfjoe PUTREG(CMCMD, CM_TX(buffer)); 65489099Sfjoe /* init watchdog timer */ 655199559Sjhb sc->sc_timer = ARCTIMEOUT; 65689099Sfjoe 65789099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 658104251Sbrooks if_printf(ifp, 659104251Sbrooks "tint: starting tx on buffer %d, status 0x%02x\n", 660104251Sbrooks buffer, GETREG(CMSTAT)); 66189099Sfjoe#endif 66289099Sfjoe } else { 66389099Sfjoe /* have to disable TX interrupt */ 66489099Sfjoe sc->sc_intmask &= ~CM_TA; 66589099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 66689099Sfjoe /* ... and watchdog timer */ 667199559Sjhb sc->sc_timer = 0; 66889099Sfjoe 66989099Sfjoe#ifdef CM_DEBUG 670104251Sbrooks if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n", 671104251Sbrooks GETREG(CMSTAT)); 67289099Sfjoe#endif 67389099Sfjoe } 67489099Sfjoe 67589099Sfjoe /* XXXX TODO */ 67689099Sfjoe#ifdef CMSOFTCOPY 67789099Sfjoe /* schedule soft int to fill a new buffer for us */ 67889099Sfjoe softintr_schedule(sc->sc_txcookie); 67989099Sfjoe#else 68089099Sfjoe /* call it directly */ 681159529Sfjoe cm_start_locked(ifp); 68289099Sfjoe#endif 68389099Sfjoe} 68489099Sfjoe 68589099Sfjoe/* 68689099Sfjoe * Our interrupt routine 68789099Sfjoe */ 68889099Sfjoevoid 68989099Sfjoecmintr(arg) 69089099Sfjoe void *arg; 69189099Sfjoe{ 69289099Sfjoe struct cm_softc *sc = arg; 693147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 69489099Sfjoe 69589099Sfjoe u_char isr, maskedisr; 69689099Sfjoe int buffer; 69789099Sfjoe u_long newsec; 69889099Sfjoe 699159529Sfjoe CM_LOCK(sc); 700159529Sfjoe 70189099Sfjoe isr = GETREG(CMSTAT); 70289099Sfjoe maskedisr = isr & sc->sc_intmask; 703159529Sfjoe if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 704159529Sfjoe CM_UNLOCK(sc); 70589099Sfjoe return; 706159529Sfjoe } 707159529Sfjoe 70889099Sfjoe do { 70989099Sfjoe 710109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 711104251Sbrooks if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n", 712104251Sbrooks isr, sc->sc_intmask); 71389099Sfjoe#endif 71489099Sfjoe 71589099Sfjoe if (maskedisr & CM_POR) { 71689099Sfjoe /* 71789099Sfjoe * XXX We should never see this. Don't bother to store 71889099Sfjoe * the address. 719147256Sbrooks * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF); 72089099Sfjoe */ 72189099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_POR)); 72289099Sfjoe log(LOG_WARNING, 723121816Sbrooks "%s: intr: got spurious power on reset int\n", 724121816Sbrooks ifp->if_xname); 72589099Sfjoe } 72689099Sfjoe 72789099Sfjoe if (maskedisr & CM_RECON) { 72889099Sfjoe /* 72989099Sfjoe * we dont need to: 73089099Sfjoe * PUTREG(CMCMD, CM_CONF(CONF_LONG)); 73189099Sfjoe */ 73289099Sfjoe PUTREG(CMCMD, CM_CLR(CLR_RECONFIG)); 733271849Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); 73489099Sfjoe 73589099Sfjoe /* 73689099Sfjoe * If less than 2 seconds per reconfig: 73789099Sfjoe * If ARC_EXCESSIVE_RECONFIGS 73889099Sfjoe * since last burst, complain and set treshold for 73989099Sfjoe * warnings to ARC_EXCESSIVE_RECONS_REWARN. 74089099Sfjoe * 74189099Sfjoe * This allows for, e.g., new stations on the cable, or 74289099Sfjoe * cable switching as long as it is over after 74389099Sfjoe * (normally) 16 seconds. 74489099Sfjoe * 74589099Sfjoe * XXX TODO: check timeout bits in status word and 74689099Sfjoe * double time if necessary. 74789099Sfjoe */ 74889099Sfjoe 74989099Sfjoe callout_stop(&sc->sc_recon_ch); 75089099Sfjoe newsec = time_second; 75189099Sfjoe if ((newsec - sc->sc_recontime <= 2) && 75289099Sfjoe (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) { 75389099Sfjoe log(LOG_WARNING, 754121816Sbrooks "%s: excessive token losses, " 75589099Sfjoe "cable problem?\n", 756121816Sbrooks ifp->if_xname); 75789099Sfjoe } 75889099Sfjoe sc->sc_recontime = newsec; 75989099Sfjoe callout_reset(&sc->sc_recon_ch, 15 * hz, 760159529Sfjoe cm_reconwatch_locked, (void *)sc); 76189099Sfjoe } 76289099Sfjoe 76389099Sfjoe if (maskedisr & CM_RI) { 76489099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 765104251Sbrooks if_printf(ifp, "intr: hard rint, act %d\n", 766104251Sbrooks sc->sc_rx_act); 76789099Sfjoe#endif 76889099Sfjoe 76989099Sfjoe buffer = sc->sc_rx_act; 77089099Sfjoe /* look if buffer is marked invalid: */ 771109771Sfjoe if (GETMEM(buffer * 512) == 0) { 77289099Sfjoe /* 77389099Sfjoe * invalid marked buffer (or illegally 77489099Sfjoe * configured sender) 77589099Sfjoe */ 77689099Sfjoe log(LOG_WARNING, 777172568Skevlo "%s: spurious RX interrupt or sender 0 " 778121816Sbrooks " (ignored)\n", ifp->if_xname); 77989099Sfjoe /* 78089099Sfjoe * restart receiver on same buffer. 78189099Sfjoe * XXX maybe better reset interface? 78289099Sfjoe */ 78389099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 78489099Sfjoe } else { 78589099Sfjoe if (++sc->sc_rx_fillcount > 1) { 78689099Sfjoe sc->sc_intmask &= ~CM_RI; 78789099Sfjoe PUTREG(CMSTAT, sc->sc_intmask); 78889099Sfjoe } else { 78989099Sfjoe buffer ^= 1; 79089099Sfjoe sc->sc_rx_act = buffer; 79189099Sfjoe 79289099Sfjoe /* 79389099Sfjoe * Start receiver on other receive 79489099Sfjoe * buffer. This also clears the RI 795172568Skevlo * interrupt flag. 79689099Sfjoe */ 79789099Sfjoe PUTREG(CMCMD, CM_RXBC(buffer)); 79889099Sfjoe /* in RX intr, so mask is ok for RX */ 79989099Sfjoe 80089099Sfjoe#ifdef CM_DEBUG 801104251Sbrooks if_printf(ifp, "strt rx for buf %d, " 80289099Sfjoe "stat 0x%02x\n", 80389099Sfjoe sc->sc_rx_act, GETREG(CMSTAT)); 80489099Sfjoe#endif 80589099Sfjoe } 80689099Sfjoe 80789099Sfjoe#ifdef CMSOFTCOPY 80889099Sfjoe /* 80989099Sfjoe * this one starts a soft int to copy out 81089099Sfjoe * of the hw 81189099Sfjoe */ 81289099Sfjoe softintr_schedule(sc->sc_rxcookie); 81389099Sfjoe#else 81489099Sfjoe /* this one does the copy here */ 815159529Sfjoe cm_srint_locked(sc); 81689099Sfjoe#endif 81789099Sfjoe } 81889099Sfjoe } 81989099Sfjoe if (maskedisr & CM_TA) { 820159529Sfjoe cm_tint_locked(sc, isr); 82189099Sfjoe } 82289099Sfjoe isr = GETREG(CMSTAT); 82389099Sfjoe maskedisr = isr & sc->sc_intmask; 82489099Sfjoe } while (maskedisr); 825109771Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 1) 826104251Sbrooks if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n", 827104251Sbrooks isr, sc->sc_intmask); 82889099Sfjoe#endif 829159529Sfjoe CM_UNLOCK(sc); 83089099Sfjoe} 83189099Sfjoe 83289099Sfjoevoid 833159529Sfjoecm_reconwatch_locked(arg) 83489099Sfjoe void *arg; 83589099Sfjoe{ 83689099Sfjoe struct cm_softc *sc = arg; 837147256Sbrooks struct ifnet *ifp = sc->sc_ifp; 83889099Sfjoe 83989099Sfjoe if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) { 84089099Sfjoe sc->sc_reconcount = 0; 841121816Sbrooks log(LOG_WARNING, "%s: token valid again.\n", 842121816Sbrooks ifp->if_xname); 84389099Sfjoe } 84489099Sfjoe sc->sc_reconcount = 0; 84589099Sfjoe} 84689099Sfjoe 84789099Sfjoe 84889099Sfjoe/* 84989099Sfjoe * Process an ioctl request. 85089099Sfjoe * This code needs some work - it looks pretty ugly. 85189099Sfjoe */ 85289099Sfjoeint 85389099Sfjoecm_ioctl(ifp, command, data) 85489099Sfjoe struct ifnet *ifp; 85589099Sfjoe u_long command; 85689099Sfjoe caddr_t data; 85789099Sfjoe{ 85889099Sfjoe struct cm_softc *sc; 859159529Sfjoe int error; 86089099Sfjoe 86189099Sfjoe error = 0; 86289099Sfjoe sc = ifp->if_softc; 86389099Sfjoe 86489099Sfjoe#if defined(CM_DEBUG) && (CM_DEBUG > 2) 865104251Sbrooks if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command); 86689099Sfjoe#endif 86789099Sfjoe 86889099Sfjoe switch (command) { 86989099Sfjoe case SIOCSIFADDR: 870109771Sfjoe case SIOCGIFADDR: 87189099Sfjoe case SIOCADDMULTI: 87289099Sfjoe case SIOCDELMULTI: 87389099Sfjoe case SIOCSIFMTU: 87489099Sfjoe error = arc_ioctl(ifp, command, data); 87589099Sfjoe break; 87689099Sfjoe 87789099Sfjoe case SIOCSIFFLAGS: 878159529Sfjoe CM_LOCK(sc); 87989099Sfjoe if ((ifp->if_flags & IFF_UP) == 0 && 880148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 88189099Sfjoe /* 88289099Sfjoe * If interface is marked down and it is running, 88389099Sfjoe * then stop it. 88489099Sfjoe */ 885159529Sfjoe cm_stop_locked(sc); 886148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 88789099Sfjoe } else if ((ifp->if_flags & IFF_UP) != 0 && 888148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 88989099Sfjoe /* 89089099Sfjoe * If interface is marked up and it is stopped, then 89189099Sfjoe * start it. 89289099Sfjoe */ 893159529Sfjoe cm_init_locked(sc); 89489099Sfjoe } 895159529Sfjoe CM_UNLOCK(sc); 89689099Sfjoe break; 89789099Sfjoe 89889099Sfjoe default: 89989099Sfjoe error = EINVAL; 90089099Sfjoe break; 90189099Sfjoe } 90289099Sfjoe 90389099Sfjoe return (error); 90489099Sfjoe} 90589099Sfjoe 90689099Sfjoe/* 90789099Sfjoe * watchdog routine for transmitter. 90889099Sfjoe * 90989099Sfjoe * We need this, because else a receiver whose hardware is alive, but whose 91089099Sfjoe * software has not enabled the Receiver, would make our hardware wait forever 91189099Sfjoe * Discovered this after 20 times reading the docs. 91289099Sfjoe * 913108470Sschweikh * Only thing we do is disable transmitter. We'll get a transmit timeout, 91489099Sfjoe * and the int handler will have to decide not to retransmit (in case 91589099Sfjoe * retransmission is implemented). 91689099Sfjoe */ 91789099Sfjoevoid 918199559Sjhbcm_watchdog(void *arg) 91989099Sfjoe{ 920199559Sjhb struct cm_softc *sc; 92189099Sfjoe 922199559Sjhb sc = arg; 923199559Sjhb callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc); 924199559Sjhb if (sc->sc_timer == 0 || --sc->sc_timer > 0) 925199559Sjhb return; 92689099Sfjoe PUTREG(CMCMD, CM_TXDIS); 92789099Sfjoe} 928