smc90cx6.c revision 199559
1/*	$NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */
2
3#include <sys/cdefs.h>
4__FBSDID("$FreeBSD: head/sys/dev/cm/smc90cx6.c 199559 2009-11-19 22:06:40Z jhb $");
5
6/*-
7 * Copyright (c) 1994, 1995, 1998 The NetBSD Foundation, Inc.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Ignatios Souvatzis.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *        This product includes software developed by the NetBSD
24 *        Foundation, Inc. and its contributors.
25 * 4. Neither the name of The NetBSD Foundation nor the names of its
26 *    contributors may be used to endorse or promote products derived
27 *    from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 */
41
42/*
43 * Chip core driver for the SMC90c26 / SMC90c56 (and SMC90c66 in '56
44 * compatibility mode) boards
45 */
46
47/* #define CMSOFTCOPY */
48#define CMRETRANSMIT /**/
49/* #define CM_DEBUG */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/sockio.h>
54#include <sys/mbuf.h>
55#include <sys/module.h>
56#include <sys/kernel.h>
57#include <sys/socket.h>
58#include <sys/syslog.h>
59#include <sys/bus.h>
60
61#include <machine/bus.h>
62#include <sys/rman.h>
63#include <machine/resource.h>
64
65#include <net/if.h>
66#include <net/if_dl.h>
67#include <net/if_types.h>
68#include <net/if_arc.h>
69
70#include <dev/cm/smc90cx6reg.h>
71#include <dev/cm/smc90cx6var.h>
72
73MODULE_DEPEND(if_cm, arcnet, 1, 1, 1);
74
75/* these should be elsewhere */
76
77#define ARC_MIN_LEN 1
78#define ARC_MIN_FORBID_LEN 254
79#define ARC_MAX_FORBID_LEN 256
80#define ARC_MAX_LEN 508
81#define ARC_ADDR_LEN 1
82
83/* for watchdog timer. This should be more than enough. */
84#define ARCTIMEOUT (5*IFNET_SLOWHZ)
85
86devclass_t cm_devclass;
87
88/*
89 * This currently uses 2 bufs for tx, 2 for rx
90 *
91 * New rx protocol:
92 *
93 * rx has a fillcount variable. If fillcount > (NRXBUF-1),
94 * rx can be switched off from rx hard int.
95 * Else rx is restarted on the other receiver.
96 * rx soft int counts down. if it is == (NRXBUF-1), it restarts
97 * the receiver.
98 * To ensure packet ordering (we need that for 1201 later), we have a counter
99 * which is incremented modulo 256 on each receive and a per buffer
100 * variable, which is set to the counter on filling. The soft int can
101 * compare both values to determine the older packet.
102 *
103 * Transmit direction:
104 *
105 * cm_start checks tx_fillcount
106 * case 2: return
107 *
108 * else fill tx_act ^ 1 && inc tx_fillcount
109 *
110 * check tx_fillcount again.
111 * case 2: set IFF_DRV_OACTIVE to stop arc_output from filling us.
112 * case 1: start tx
113 *
114 * tint clears IFF_OACTIVE, decrements and checks tx_fillcount
115 * case 1: start tx on tx_act ^ 1, softcall cm_start
116 * case 0: softcall cm_start
117 *
118 * #define fill(i) get mbuf && copy mbuf to chip(i)
119 */
120
121void	cm_init(void *);
122static void cm_init_locked(struct cm_softc *);
123static void cm_reset_locked(struct cm_softc *);
124void	cm_start(struct ifnet *);
125void	cm_start_locked(struct ifnet *);
126int	cm_ioctl(struct ifnet *, unsigned long, caddr_t);
127void	cm_watchdog(void *);
128void	cm_srint_locked(void *vsc);
129static	void cm_tint_locked(struct cm_softc *, int);
130void	cm_reconwatch_locked(void *);
131
132/*
133 * Release all resources
134 */
135void
136cm_release_resources(dev)
137	device_t dev;
138{
139	struct cm_softc *sc = device_get_softc(dev);
140
141	if (sc->port_res != NULL) {
142		bus_release_resource(dev, SYS_RES_IOPORT,
143				     0, sc->port_res);
144		sc->port_res = NULL;
145	}
146	if (sc->mem_res != NULL) {
147		bus_release_resource(dev, SYS_RES_MEMORY,
148				     0, sc->mem_res);
149		sc->mem_res = NULL;
150	}
151	if (sc->irq_res != NULL) {
152		bus_release_resource(dev, SYS_RES_IRQ,
153				     0, sc->irq_res);
154		sc->irq_res = NULL;
155	}
156}
157
158int
159cm_attach(dev)
160	device_t dev;
161{
162	struct cm_softc *sc = device_get_softc(dev);
163	struct ifnet *ifp;
164	u_int8_t linkaddress;
165
166	ifp = sc->sc_ifp = if_alloc(IFT_ARCNET);
167	if (ifp == NULL)
168		return (ENOSPC);
169
170	/*
171	 * read the arcnet address from the board
172	 */
173	GETREG(CMRESET);
174	do {
175		DELAY(200);
176	} while (!(GETREG(CMSTAT) & CM_POR));
177	linkaddress = GETMEM(CMMACOFF);
178
179	/* clear the int mask... */
180	sc->sc_intmask = 0;
181	PUTREG(CMSTAT, 0);
182
183	PUTREG(CMCMD, CM_CONF(CONF_LONG));
184	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
185	sc->sc_recontime = sc->sc_reconcount = 0;
186
187	/*
188	 * set interface to stopped condition (reset)
189	 */
190	cm_stop_locked(sc);
191
192	ifp->if_softc = sc;
193	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
194	ifp->if_output = arc_output;
195	ifp->if_start = cm_start;
196	ifp->if_ioctl = cm_ioctl;
197	ifp->if_init = cm_init;
198	/* XXX IFQ_SET_READY(&ifp->if_snd); */
199	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
200	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
201
202	arc_ifattach(ifp, linkaddress);
203
204#ifdef CMSOFTCOPY
205	sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc);
206	sc->sc_txcookie = softintr_establish(IPL_SOFTNET,
207		(void (*)(void *))cm_start, ifp);
208#endif
209
210	callout_init_mtx(&sc->sc_recon_ch, &sc->sc_mtx, 0);
211	callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0);
212
213	if_printf(ifp, "link addr 0x%02x (%d)\n", linkaddress, linkaddress);
214	return 0;
215}
216
217/*
218 * Initialize device
219 *
220 */
221void
222cm_init(xsc)
223	void *xsc;
224{
225	struct cm_softc *sc = (struct cm_softc *)xsc;
226
227	CM_LOCK(sc);
228	cm_init_locked(sc);
229	CM_UNLOCK(sc);
230}
231
232static void
233cm_init_locked(struct cm_softc *sc)
234{
235	struct ifnet *ifp = sc->sc_ifp;
236
237	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
238		ifp->if_drv_flags |= IFF_DRV_RUNNING;
239		cm_reset_locked(sc);
240	}
241}
242
243/*
244 * Reset the interface...
245 *
246 * Assumes that it is called with sc_mtx held
247 */
248void
249cm_reset_locked(sc)
250	struct cm_softc *sc;
251{
252	struct ifnet *ifp;
253	int linkaddress;
254
255	ifp = sc->sc_ifp;
256
257#ifdef CM_DEBUG
258	if_printf(ifp, "reset\n");
259#endif
260	/* stop and restart hardware */
261
262	GETREG(CMRESET);
263	do {
264		DELAY(200);
265	} while (!(GETREG(CMSTAT) & CM_POR));
266
267	linkaddress = GETMEM(CMMACOFF);
268
269#if defined(CM_DEBUG) && (CM_DEBUG > 2)
270	if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n",
271	    linkaddress, linkaddress);
272#endif
273
274	/* tell the routing level about the (possibly changed) link address */
275	arc_storelladdr(ifp, linkaddress);
276	arc_frag_init(ifp);
277
278	/* POR is NMI, but we need it below: */
279	sc->sc_intmask = CM_RECON|CM_POR;
280	PUTREG(CMSTAT, sc->sc_intmask);
281	PUTREG(CMCMD, CM_CONF(CONF_LONG));
282
283#ifdef CM_DEBUG
284	if_printf(ifp, "reset: chip configured, status=0x%02x\n",
285	    GETREG(CMSTAT));
286#endif
287	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
288
289#ifdef CM_DEBUG
290	if_printf(ifp, "reset: bits cleared, status=0x%02x\n",
291	     GETREG(CMSTAT));
292#endif
293
294	sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS;
295
296	/* start receiver */
297
298	sc->sc_intmask  |= CM_RI;
299	sc->sc_rx_fillcount = 0;
300	sc->sc_rx_act = 2;
301
302	PUTREG(CMCMD, CM_RXBC(2));
303	PUTREG(CMSTAT, sc->sc_intmask);
304
305#ifdef CM_DEBUG
306	if_printf(ifp, "reset: started receiver, status=0x%02x\n",
307	    GETREG(CMSTAT));
308#endif
309
310	/* and init transmitter status */
311	sc->sc_tx_act = 0;
312	sc->sc_tx_fillcount = 0;
313
314	ifp->if_drv_flags |= IFF_DRV_RUNNING;
315	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
316
317	callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc);
318	cm_start_locked(ifp);
319}
320
321/*
322 * Take interface offline
323 */
324void
325cm_stop_locked(sc)
326	struct cm_softc *sc;
327{
328	/* Stop the interrupts */
329	PUTREG(CMSTAT, 0);
330
331	/* Stop the interface */
332	GETREG(CMRESET);
333
334	/* Stop watchdog timer */
335	callout_stop(&sc->sc_watchdog_timer);
336	sc->sc_timer = 0;
337}
338
339void
340cm_start(struct ifnet *ifp)
341{
342	struct cm_softc *sc = ifp->if_softc;
343
344	CM_LOCK(sc);
345	cm_start_locked(ifp);
346	CM_UNLOCK(sc);
347}
348
349/*
350 * Start output on interface. Get another datagram to send
351 * off the interface queue, and copy it to the
352 * interface becore starting the output
353 *
354 * Assumes that sc_mtx is held
355 */
356void
357cm_start_locked(ifp)
358	struct ifnet *ifp;
359{
360	struct cm_softc *sc = ifp->if_softc;
361	struct mbuf *m, *mp;
362
363	int cm_ram_ptr;
364	int len, tlen, offset, buffer;
365#ifdef CMTIMINGS
366	u_long copystart, lencopy, perbyte;
367#endif
368
369#if defined(CM_DEBUG) && (CM_DEBUG > 3)
370	if_printf(ifp, "start(%p)\n", ifp);
371#endif
372
373	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
374		return;
375
376	if (sc->sc_tx_fillcount >= 2)
377		return;
378
379	m = arc_frag_next(ifp);
380	buffer = sc->sc_tx_act ^ 1;
381
382	if (m == 0)
383		return;
384
385#ifdef CM_DEBUG
386	if (m->m_len < ARC_HDRLEN)
387		m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */
388	if_printf(ifp, "start: filling %d from %d to %d type %d\n",
389	    buffer, mtod(m, u_char *)[0],
390	    mtod(m, u_char *)[1], mtod(m, u_char *)[2]);
391#else
392	if (m->m_len < 2)
393		m = m_pullup(m, 2);
394#endif
395	cm_ram_ptr = buffer * 512;
396
397	if (m == 0)
398		return;
399
400	/* write the addresses to RAM and throw them away */
401
402	/*
403	 * Hardware does this: Yet Another Microsecond Saved.
404	 * (btw, timing code says usually 2 microseconds)
405	 * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]);
406	 */
407
408	PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]);
409	m_adj(m, 2);
410
411	/* get total length left at this point */
412	tlen = m->m_pkthdr.len;
413	if (tlen < ARC_MIN_FORBID_LEN) {
414		offset = 256 - tlen;
415		PUTMEM(cm_ram_ptr + 2, offset);
416	} else {
417		PUTMEM(cm_ram_ptr + 2, 0);
418		if (tlen <= ARC_MAX_FORBID_LEN)
419			offset = 255;		/* !!! */
420		else {
421			if (tlen > ARC_MAX_LEN)
422				tlen = ARC_MAX_LEN;
423			offset = 512 - tlen;
424		}
425		PUTMEM(cm_ram_ptr + 3, offset);
426
427	}
428	cm_ram_ptr += offset;
429
430	/* lets loop through the mbuf chain */
431
432	for (mp = m; mp; mp = mp->m_next) {
433		if ((len = mp->m_len)) {		/* YAMS */
434			bus_space_write_region_1(
435			    rman_get_bustag(sc->mem_res),
436			    rman_get_bushandle(sc->mem_res),
437			    cm_ram_ptr, mtod(mp, caddr_t), len);
438
439			cm_ram_ptr += len;
440		}
441	}
442
443	sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0;
444	sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5;
445
446	if (++sc->sc_tx_fillcount > 1) {
447		/*
448		 * We are filled up to the rim. No more bufs for the moment,
449		 * please.
450		 */
451		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
452	} else {
453#ifdef CM_DEBUG
454		if_printf(ifp, "start: starting transmitter on buffer %d\n",
455		    buffer);
456#endif
457		/* Transmitter was off, start it */
458		sc->sc_tx_act = buffer;
459
460		/*
461		 * We still can accept another buf, so don't:
462		 * ifp->if_drv_flags |= IFF_DRV_OACTIVE;
463		 */
464		sc->sc_intmask |= CM_TA;
465		PUTREG(CMCMD, CM_TX(buffer));
466		PUTREG(CMSTAT, sc->sc_intmask);
467
468		sc->sc_timer = ARCTIMEOUT;
469	}
470	m_freem(m);
471
472	/*
473	 * After 10 times reading the docs, I realized
474	 * that in the case the receiver NAKs the buffer request,
475	 * the hardware retries till shutdown.
476	 * This is integrated now in the code above.
477	 */
478}
479
480#ifdef CMSOFTCOPY
481void
482cm_srint(void *vsc)
483{
484	struct cm_softc *sc = (struct cm_softc *)vsc;
485
486	CM_LOCK(sc);
487	cm_srint_locked(vsc);
488	CM_UNLOCK(sc);
489}
490#endif
491
492/*
493 * Arcnet interface receiver soft interrupt:
494 * get the stuff out of any filled buffer we find.
495 */
496void
497cm_srint_locked(vsc)
498	void *vsc;
499{
500	struct cm_softc *sc = (struct cm_softc *)vsc;
501	int buffer, len, offset, type;
502	int cm_ram_ptr;
503	struct mbuf *m;
504	struct arc_header *ah;
505	struct ifnet *ifp;
506
507	ifp = sc->sc_ifp;
508
509	buffer = sc->sc_rx_act ^ 1;
510
511	/* Allocate header mbuf */
512	MGETHDR(m, M_DONTWAIT, MT_DATA);
513
514	if (m == 0) {
515		/*
516		 * in case s.th. goes wrong with mem, drop it
517		 * to make sure the receiver can be started again
518		 * count it as input error (we dont have any other
519		 * detectable)
520		 */
521		ifp->if_ierrors++;
522		goto cleanup;
523	}
524
525	m->m_pkthdr.rcvif = ifp;
526
527	/*
528	 * Align so that IP packet will be longword aligned. Here we
529	 * assume that m_data of new packet is longword aligned.
530	 * When implementing PHDS, we might have to change it to 2,
531	 * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent.
532	 */
533
534	cm_ram_ptr = buffer * 512;
535	offset = GETMEM(cm_ram_ptr + 2);
536	if (offset)
537		len = 256 - offset;
538	else {
539		offset = GETMEM(cm_ram_ptr + 3);
540		len = 512 - offset;
541	}
542
543	/*
544	 * first +2 bytes for align fixup below
545	 * second +2 bytes are for src/dst addresses
546	 */
547	if ((len + 2 + 2) > MHLEN) {
548		/* attach an mbuf cluster */
549		MCLGET(m, M_DONTWAIT);
550
551		/* Insist on getting a cluster */
552		if ((m->m_flags & M_EXT) == 0) {
553			ifp->if_ierrors++;
554			goto cleanup;
555		}
556	}
557
558	if (m == 0) {
559		ifp->if_ierrors++;
560		goto cleanup;
561	}
562
563	type = GETMEM(cm_ram_ptr + offset);
564	m->m_data += 1 + arc_isphds(type);
565	/* mbuf filled with ARCnet addresses */
566	m->m_pkthdr.len = m->m_len = len + 2;
567
568	ah = mtod(m, struct arc_header *);
569	ah->arc_shost = GETMEM(cm_ram_ptr + 0);
570	ah->arc_dhost = GETMEM(cm_ram_ptr + 1);
571
572	bus_space_read_region_1(
573	    rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res),
574	    cm_ram_ptr + offset, mtod(m, u_char *) + 2, len);
575
576	CM_UNLOCK(sc);
577	arc_input(ifp, m);
578	CM_LOCK(sc);
579
580	m = NULL;
581	ifp->if_ipackets++;
582
583cleanup:
584
585	if (m != NULL)
586		m_freem(m);
587
588	/* mark buffer as invalid by source id 0 */
589	PUTMEM(buffer << 9, 0);
590	if (--sc->sc_rx_fillcount == 2 - 1) {
591
592		/* was off, restart it on buffer just emptied */
593		sc->sc_rx_act = buffer;
594		sc->sc_intmask |= CM_RI;
595
596		/* this also clears the RI flag interrupt: */
597		PUTREG(CMCMD, CM_RXBC(buffer));
598		PUTREG(CMSTAT, sc->sc_intmask);
599
600#ifdef CM_DEBUG
601		if_printf(ifp, "srint: restarted rx on buf %d\n", buffer);
602#endif
603	}
604}
605
606__inline static void
607cm_tint_locked(sc, isr)
608	struct cm_softc *sc;
609	int isr;
610{
611	struct ifnet *ifp;
612
613	int buffer;
614#ifdef CMTIMINGS
615	int clknow;
616#endif
617
618	ifp = sc->sc_ifp;
619	buffer = sc->sc_tx_act;
620
621	/*
622	 * retransmit code:
623	 * Normal situtations first for fast path:
624	 * If acknowledgement received ok or broadcast, we're ok.
625	 * else if
626	 */
627
628	if (isr & CM_TMA || sc->sc_broadcast[buffer])
629		ifp->if_opackets++;
630#ifdef CMRETRANSMIT
631	else if (ifp->if_flags & IFF_LINK2 && sc->sc_timer > 0
632	    && --sc->sc_retransmits[buffer] > 0) {
633		/* retransmit same buffer */
634		PUTREG(CMCMD, CM_TX(buffer));
635		return;
636	}
637#endif
638	else
639		ifp->if_oerrors++;
640
641
642	/* We know we can accept another buffer at this point. */
643	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
644
645	if (--sc->sc_tx_fillcount > 0) {
646
647		/*
648		 * start tx on other buffer.
649		 * This also clears the int flag
650		 */
651		buffer ^= 1;
652		sc->sc_tx_act = buffer;
653
654		/*
655		 * already given:
656		 * sc->sc_intmask |= CM_TA;
657		 * PUTREG(CMSTAT, sc->sc_intmask);
658		 */
659		PUTREG(CMCMD, CM_TX(buffer));
660		/* init watchdog timer */
661		sc->sc_timer = ARCTIMEOUT;
662
663#if defined(CM_DEBUG) && (CM_DEBUG > 1)
664		if_printf(ifp,
665		    "tint: starting tx on buffer %d, status 0x%02x\n",
666		    buffer, GETREG(CMSTAT));
667#endif
668	} else {
669		/* have to disable TX interrupt */
670		sc->sc_intmask &= ~CM_TA;
671		PUTREG(CMSTAT, sc->sc_intmask);
672		/* ... and watchdog timer */
673		sc->sc_timer = 0;
674
675#ifdef CM_DEBUG
676		if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n",
677		    GETREG(CMSTAT));
678#endif
679	}
680
681	/* XXXX TODO */
682#ifdef CMSOFTCOPY
683	/* schedule soft int to fill a new buffer for us */
684	softintr_schedule(sc->sc_txcookie);
685#else
686	/* call it directly */
687	cm_start_locked(ifp);
688#endif
689}
690
691/*
692 * Our interrupt routine
693 */
694void
695cmintr(arg)
696	void *arg;
697{
698	struct cm_softc *sc = arg;
699	struct ifnet *ifp = sc->sc_ifp;
700
701	u_char isr, maskedisr;
702	int buffer;
703	u_long newsec;
704
705	CM_LOCK(sc);
706
707	isr = GETREG(CMSTAT);
708	maskedisr = isr & sc->sc_intmask;
709	if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
710		CM_UNLOCK(sc);
711		return;
712	}
713
714	do {
715
716#if defined(CM_DEBUG) && (CM_DEBUG > 1)
717		if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n",
718		    isr, sc->sc_intmask);
719#endif
720
721		if (maskedisr & CM_POR) {
722			/*
723			 * XXX We should never see this. Don't bother to store
724			 * the address.
725			 * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF);
726			 */
727			PUTREG(CMCMD, CM_CLR(CLR_POR));
728			log(LOG_WARNING,
729			    "%s: intr: got spurious power on reset int\n",
730			    ifp->if_xname);
731		}
732
733		if (maskedisr & CM_RECON) {
734			/*
735			 * we dont need to:
736			 * PUTREG(CMCMD, CM_CONF(CONF_LONG));
737			 */
738			PUTREG(CMCMD, CM_CLR(CLR_RECONFIG));
739			ifp->if_collisions++;
740
741			/*
742			 * If less than 2 seconds per reconfig:
743			 *	If ARC_EXCESSIVE_RECONFIGS
744			 *	since last burst, complain and set treshold for
745			 *	warnings to ARC_EXCESSIVE_RECONS_REWARN.
746			 *
747			 * This allows for, e.g., new stations on the cable, or
748			 * cable switching as long as it is over after
749			 * (normally) 16 seconds.
750			 *
751			 * XXX TODO: check timeout bits in status word and
752			 * double time if necessary.
753			 */
754
755			callout_stop(&sc->sc_recon_ch);
756			newsec = time_second;
757			if ((newsec - sc->sc_recontime <= 2) &&
758			    (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) {
759				log(LOG_WARNING,
760				    "%s: excessive token losses, "
761				    "cable problem?\n",
762				    ifp->if_xname);
763			}
764			sc->sc_recontime = newsec;
765			callout_reset(&sc->sc_recon_ch, 15 * hz,
766			    cm_reconwatch_locked, (void *)sc);
767		}
768
769		if (maskedisr & CM_RI) {
770#if defined(CM_DEBUG) && (CM_DEBUG > 1)
771			if_printf(ifp, "intr: hard rint, act %d\n",
772			    sc->sc_rx_act);
773#endif
774
775			buffer = sc->sc_rx_act;
776			/* look if buffer is marked invalid: */
777			if (GETMEM(buffer * 512) == 0) {
778				/*
779				 * invalid marked buffer (or illegally
780				 * configured sender)
781				 */
782				log(LOG_WARNING,
783				    "%s: spurious RX interrupt or sender 0 "
784				    " (ignored)\n", ifp->if_xname);
785				/*
786				 * restart receiver on same buffer.
787				 * XXX maybe better reset interface?
788				 */
789				PUTREG(CMCMD, CM_RXBC(buffer));
790			} else {
791				if (++sc->sc_rx_fillcount > 1) {
792					sc->sc_intmask &= ~CM_RI;
793					PUTREG(CMSTAT, sc->sc_intmask);
794				} else {
795					buffer ^= 1;
796					sc->sc_rx_act = buffer;
797
798					/*
799					 * Start receiver on other receive
800					 * buffer. This also clears the RI
801					 * interrupt flag.
802					 */
803					PUTREG(CMCMD, CM_RXBC(buffer));
804					/* in RX intr, so mask is ok for RX */
805
806#ifdef CM_DEBUG
807					if_printf(ifp, "strt rx for buf %d, "
808					    "stat 0x%02x\n",
809					    sc->sc_rx_act, GETREG(CMSTAT));
810#endif
811				}
812
813#ifdef CMSOFTCOPY
814				/*
815				 * this one starts a soft int to copy out
816				 * of the hw
817				 */
818				softintr_schedule(sc->sc_rxcookie);
819#else
820				/* this one does the copy here */
821				cm_srint_locked(sc);
822#endif
823			}
824		}
825		if (maskedisr & CM_TA) {
826			cm_tint_locked(sc, isr);
827		}
828		isr = GETREG(CMSTAT);
829		maskedisr = isr & sc->sc_intmask;
830	} while (maskedisr);
831#if defined(CM_DEBUG) && (CM_DEBUG > 1)
832	if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n",
833	    isr, sc->sc_intmask);
834#endif
835	CM_UNLOCK(sc);
836}
837
838void
839cm_reconwatch_locked(arg)
840	void *arg;
841{
842	struct cm_softc *sc = arg;
843	struct ifnet *ifp = sc->sc_ifp;
844
845	if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) {
846		sc->sc_reconcount = 0;
847		log(LOG_WARNING, "%s: token valid again.\n",
848		    ifp->if_xname);
849	}
850	sc->sc_reconcount = 0;
851}
852
853
854/*
855 * Process an ioctl request.
856 * This code needs some work - it looks pretty ugly.
857 */
858int
859cm_ioctl(ifp, command, data)
860	struct ifnet *ifp;
861	u_long command;
862	caddr_t data;
863{
864	struct cm_softc *sc;
865	int error;
866
867	error = 0;
868	sc = ifp->if_softc;
869
870#if defined(CM_DEBUG) && (CM_DEBUG > 2)
871	if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command);
872#endif
873
874	switch (command) {
875	case SIOCSIFADDR:
876	case SIOCGIFADDR:
877	case SIOCADDMULTI:
878	case SIOCDELMULTI:
879	case SIOCSIFMTU:
880		error = arc_ioctl(ifp, command, data);
881		break;
882
883	case SIOCSIFFLAGS:
884		CM_LOCK(sc);
885		if ((ifp->if_flags & IFF_UP) == 0 &&
886		    (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
887			/*
888			 * If interface is marked down and it is running,
889			 * then stop it.
890			 */
891			cm_stop_locked(sc);
892			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
893		} else if ((ifp->if_flags & IFF_UP) != 0 &&
894			   (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
895			/*
896			 * If interface is marked up and it is stopped, then
897			 * start it.
898			 */
899			cm_init_locked(sc);
900		}
901		CM_UNLOCK(sc);
902		break;
903
904	default:
905		error = EINVAL;
906		break;
907	}
908
909	return (error);
910}
911
912/*
913 * watchdog routine for transmitter.
914 *
915 * We need this, because else a receiver whose hardware is alive, but whose
916 * software has not enabled the Receiver, would make our hardware wait forever
917 * Discovered this after 20 times reading the docs.
918 *
919 * Only thing we do is disable transmitter. We'll get a transmit timeout,
920 * and the int handler will have to decide not to retransmit (in case
921 * retransmission is implemented).
922 */
923void
924cm_watchdog(void *arg)
925{
926	struct cm_softc *sc;
927
928	sc = arg;
929	callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc);
930	if (sc->sc_timer == 0 || --sc->sc_timer > 0)
931		return;
932	PUTREG(CMCMD, CM_TXDIS);
933}
934