smc90cx6.c revision 159529
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 159529 2006-06-11 22:25:01Z fjoe $");
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(struct ifnet *);
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_deactivate_resource(dev, SYS_RES_IOPORT,
143				     0, sc->port_res);
144		bus_release_resource(dev, SYS_RES_IOPORT,
145				     0, sc->port_res);
146		sc->port_res = NULL;
147	}
148	if (sc->mem_res != NULL) {
149		bus_deactivate_resource(dev, SYS_RES_MEMORY,
150				     0, sc->mem_res);
151		bus_release_resource(dev, SYS_RES_MEMORY,
152				     0, sc->mem_res);
153		sc->mem_res = NULL;
154	}
155	if (sc->irq_res != NULL) {
156		bus_deactivate_resource(dev, SYS_RES_IRQ,
157				     0, sc->irq_res);
158		bus_release_resource(dev, SYS_RES_IRQ,
159				     0, sc->irq_res);
160		sc->irq_res = NULL;
161	}
162}
163
164int
165cm_attach(dev)
166	device_t dev;
167{
168	struct cm_softc *sc = device_get_softc(dev);
169	struct ifnet *ifp;
170	u_int8_t linkaddress;
171
172	ifp = sc->sc_ifp = if_alloc(IFT_ARCNET);
173	if (ifp == NULL)
174		return (ENOSPC);
175
176	/*
177	 * read the arcnet address from the board
178	 */
179	GETREG(CMRESET);
180	do {
181		DELAY(200);
182	} while (!(GETREG(CMSTAT) & CM_POR));
183	linkaddress = GETMEM(CMMACOFF);
184
185	/* clear the int mask... */
186	sc->sc_intmask = 0;
187	PUTREG(CMSTAT, 0);
188
189	PUTREG(CMCMD, CM_CONF(CONF_LONG));
190	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
191	sc->sc_recontime = sc->sc_reconcount = 0;
192
193	/*
194	 * set interface to stopped condition (reset)
195	 */
196	cm_stop_locked(sc);
197
198	ifp->if_softc = sc;
199	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
200	ifp->if_output = arc_output;
201	ifp->if_start = cm_start;
202	ifp->if_ioctl = cm_ioctl;
203	ifp->if_watchdog  = cm_watchdog;
204	ifp->if_init = cm_init;
205	/* XXX IFQ_SET_READY(&ifp->if_snd); */
206	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
207	ifp->if_timer = 0;
208	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
209
210	arc_ifattach(ifp, linkaddress);
211
212#ifdef CMSOFTCOPY
213	sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc);
214	sc->sc_txcookie = softintr_establish(IPL_SOFTNET,
215		(void (*)(void *))cm_start, ifp);
216#endif
217
218	callout_init_mtx(&sc->sc_recon_ch, &sc->sc_mtx, 0);
219
220	if_printf(ifp, "link addr 0x%02x (%d)\n", linkaddress, linkaddress);
221	return 0;
222}
223
224/*
225 * Initialize device
226 *
227 */
228void
229cm_init(xsc)
230	void *xsc;
231{
232	struct cm_softc *sc = (struct cm_softc *)xsc;
233
234	CM_LOCK(sc);
235	cm_init_locked(sc);
236	CM_UNLOCK(sc);
237}
238
239static void
240cm_init_locked(struct cm_softc *sc)
241{
242	struct ifnet *ifp = sc->sc_ifp;
243
244	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
245		ifp->if_drv_flags |= IFF_DRV_RUNNING;
246		cm_reset_locked(sc);
247	}
248}
249
250/*
251 * Reset the interface...
252 *
253 * Assumes that it is called with sc_mtx held
254 */
255void
256cm_reset_locked(sc)
257	struct cm_softc *sc;
258{
259	struct ifnet *ifp;
260	int linkaddress;
261
262	ifp = sc->sc_ifp;
263
264#ifdef CM_DEBUG
265	if_printf(ifp, "reset\n");
266#endif
267	/* stop and restart hardware */
268
269	GETREG(CMRESET);
270	do {
271		DELAY(200);
272	} while (!(GETREG(CMSTAT) & CM_POR));
273
274	linkaddress = GETMEM(CMMACOFF);
275
276#if defined(CM_DEBUG) && (CM_DEBUG > 2)
277	if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n",
278	    linkaddress, linkaddress);
279#endif
280
281	/* tell the routing level about the (possibly changed) link address */
282	arc_storelladdr(ifp, linkaddress);
283	arc_frag_init(ifp);
284
285	/* POR is NMI, but we need it below: */
286	sc->sc_intmask = CM_RECON|CM_POR;
287	PUTREG(CMSTAT, sc->sc_intmask);
288	PUTREG(CMCMD, CM_CONF(CONF_LONG));
289
290#ifdef CM_DEBUG
291	if_printf(ifp, "reset: chip configured, status=0x%02x\n",
292	    GETREG(CMSTAT));
293#endif
294	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
295
296#ifdef CM_DEBUG
297	if_printf(ifp, "reset: bits cleared, status=0x%02x\n",
298	     GETREG(CMSTAT));
299#endif
300
301	sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS;
302
303	/* start receiver */
304
305	sc->sc_intmask  |= CM_RI;
306	sc->sc_rx_fillcount = 0;
307	sc->sc_rx_act = 2;
308
309	PUTREG(CMCMD, CM_RXBC(2));
310	PUTREG(CMSTAT, sc->sc_intmask);
311
312#ifdef CM_DEBUG
313	if_printf(ifp, "reset: started receiver, status=0x%02x\n",
314	    GETREG(CMSTAT));
315#endif
316
317	/* and init transmitter status */
318	sc->sc_tx_act = 0;
319	sc->sc_tx_fillcount = 0;
320
321	ifp->if_drv_flags |= IFF_DRV_RUNNING;
322	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
323
324	cm_start_locked(ifp);
325}
326
327/*
328 * Take interface offline
329 */
330void
331cm_stop_locked(sc)
332	struct cm_softc *sc;
333{
334	/* Stop the interrupts */
335	PUTREG(CMSTAT, 0);
336
337	/* Stop the interface */
338	GETREG(CMRESET);
339
340	/* Stop watchdog timer */
341	sc->sc_ifp->if_timer = 0;
342}
343
344void
345cm_start(struct ifnet *ifp)
346{
347	struct cm_softc *sc = ifp->if_softc;
348
349	CM_LOCK(sc);
350	cm_start_locked(ifp);
351	CM_UNLOCK(sc);
352}
353
354/*
355 * Start output on interface. Get another datagram to send
356 * off the interface queue, and copy it to the
357 * interface becore starting the output
358 *
359 * Assumes that sc_mtx is held
360 */
361void
362cm_start_locked(ifp)
363	struct ifnet *ifp;
364{
365	struct cm_softc *sc = ifp->if_softc;
366	struct mbuf *m, *mp;
367
368	int cm_ram_ptr;
369	int len, tlen, offset, buffer;
370#ifdef CMTIMINGS
371	u_long copystart, lencopy, perbyte;
372#endif
373
374#if defined(CM_DEBUG) && (CM_DEBUG > 3)
375	if_printf(ifp, "start(%p)\n", ifp);
376#endif
377
378	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
379		return;
380
381	if (sc->sc_tx_fillcount >= 2)
382		return;
383
384	m = arc_frag_next(ifp);
385	buffer = sc->sc_tx_act ^ 1;
386
387	if (m == 0)
388		return;
389
390#ifdef CM_DEBUG
391	if (m->m_len < ARC_HDRLEN)
392		m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */
393	if_printf(ifp, "start: filling %d from %d to %d type %d\n",
394	    buffer, mtod(m, u_char *)[0],
395	    mtod(m, u_char *)[1], mtod(m, u_char *)[2]);
396#else
397	if (m->m_len < 2)
398		m = m_pullup(m, 2);
399#endif
400	cm_ram_ptr = buffer * 512;
401
402	if (m == 0)
403		return;
404
405	/* write the addresses to RAM and throw them away */
406
407	/*
408	 * Hardware does this: Yet Another Microsecond Saved.
409	 * (btw, timing code says usually 2 microseconds)
410	 * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]);
411	 */
412
413	PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]);
414	m_adj(m, 2);
415
416	/* get total length left at this point */
417	tlen = m->m_pkthdr.len;
418	if (tlen < ARC_MIN_FORBID_LEN) {
419		offset = 256 - tlen;
420		PUTMEM(cm_ram_ptr + 2, offset);
421	} else {
422		PUTMEM(cm_ram_ptr + 2, 0);
423		if (tlen <= ARC_MAX_FORBID_LEN)
424			offset = 255;		/* !!! */
425		else {
426			if (tlen > ARC_MAX_LEN)
427				tlen = ARC_MAX_LEN;
428			offset = 512 - tlen;
429		}
430		PUTMEM(cm_ram_ptr + 3, offset);
431
432	}
433	cm_ram_ptr += offset;
434
435	/* lets loop through the mbuf chain */
436
437	for (mp = m; mp; mp = mp->m_next) {
438		if ((len = mp->m_len)) {		/* YAMS */
439			bus_space_write_region_1(
440			    rman_get_bustag(sc->mem_res),
441			    rman_get_bushandle(sc->mem_res),
442			    cm_ram_ptr, mtod(mp, caddr_t), len);
443
444			cm_ram_ptr += len;
445		}
446	}
447
448	sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0;
449	sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5;
450
451	if (++sc->sc_tx_fillcount > 1) {
452		/*
453		 * We are filled up to the rim. No more bufs for the moment,
454		 * please.
455		 */
456		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
457	} else {
458#ifdef CM_DEBUG
459		if_printf(ifp, "start: starting transmitter on buffer %d\n",
460		    buffer);
461#endif
462		/* Transmitter was off, start it */
463		sc->sc_tx_act = buffer;
464
465		/*
466		 * We still can accept another buf, so don't:
467		 * ifp->if_drv_flags |= IFF_DRV_OACTIVE;
468		 */
469		sc->sc_intmask |= CM_TA;
470		PUTREG(CMCMD, CM_TX(buffer));
471		PUTREG(CMSTAT, sc->sc_intmask);
472
473		ifp->if_timer = ARCTIMEOUT;
474	}
475	m_freem(m);
476
477	/*
478	 * After 10 times reading the docs, I realized
479	 * that in the case the receiver NAKs the buffer request,
480	 * the hardware retries till shutdown.
481	 * This is integrated now in the code above.
482	 */
483}
484
485#ifdef CMSOFTCOPY
486void
487cm_srint(void *vsc)
488{
489	struct cm_softc *sc = (struct cm_softc *)vsc;
490
491	CM_LOCK(sc);
492	cm_srint_locked(vsc);
493	CM_UNLOCK(sc);
494}
495#endif
496
497/*
498 * Arcnet interface receiver soft interrupt:
499 * get the stuff out of any filled buffer we find.
500 */
501void
502cm_srint_locked(vsc)
503	void *vsc;
504{
505	struct cm_softc *sc = (struct cm_softc *)vsc;
506	int buffer, len, offset, type;
507	int cm_ram_ptr;
508	struct mbuf *m;
509	struct arc_header *ah;
510	struct ifnet *ifp;
511
512	ifp = sc->sc_ifp;
513
514	buffer = sc->sc_rx_act ^ 1;
515
516	/* Allocate header mbuf */
517	MGETHDR(m, M_DONTWAIT, MT_DATA);
518
519	if (m == 0) {
520		/*
521		 * in case s.th. goes wrong with mem, drop it
522		 * to make sure the receiver can be started again
523		 * count it as input error (we dont have any other
524		 * detectable)
525		 */
526		ifp->if_ierrors++;
527		goto cleanup;
528	}
529
530	m->m_pkthdr.rcvif = ifp;
531
532	/*
533	 * Align so that IP packet will be longword aligned. Here we
534	 * assume that m_data of new packet is longword aligned.
535	 * When implementing PHDS, we might have to change it to 2,
536	 * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent.
537	 */
538
539	cm_ram_ptr = buffer * 512;
540	offset = GETMEM(cm_ram_ptr + 2);
541	if (offset)
542		len = 256 - offset;
543	else {
544		offset = GETMEM(cm_ram_ptr + 3);
545		len = 512 - offset;
546	}
547
548	/*
549	 * first +2 bytes for align fixup below
550	 * second +2 bytes are for src/dst addresses
551	 */
552	if ((len + 2 + 2) > MHLEN) {
553		/* attach an mbuf cluster */
554		MCLGET(m, M_DONTWAIT);
555
556		/* Insist on getting a cluster */
557		if ((m->m_flags & M_EXT) == 0) {
558			ifp->if_ierrors++;
559			goto cleanup;
560		}
561	}
562
563	if (m == 0) {
564		ifp->if_ierrors++;
565		goto cleanup;
566	}
567
568	type = GETMEM(cm_ram_ptr + offset);
569	m->m_data += 1 + arc_isphds(type);
570	/* mbuf filled with ARCnet addresses */
571	m->m_pkthdr.len = m->m_len = len + 2;
572
573	ah = mtod(m, struct arc_header *);
574	ah->arc_shost = GETMEM(cm_ram_ptr + 0);
575	ah->arc_dhost = GETMEM(cm_ram_ptr + 1);
576
577	bus_space_read_region_1(
578	    rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res),
579	    cm_ram_ptr + offset, mtod(m, u_char *) + 2, len);
580
581	CM_UNLOCK(sc);
582	arc_input(ifp, m);
583	CM_LOCK(sc);
584
585	m = NULL;
586	ifp->if_ipackets++;
587
588cleanup:
589
590	if (m != NULL)
591		m_freem(m);
592
593	/* mark buffer as invalid by source id 0 */
594	PUTMEM(buffer << 9, 0);
595	if (--sc->sc_rx_fillcount == 2 - 1) {
596
597		/* was off, restart it on buffer just emptied */
598		sc->sc_rx_act = buffer;
599		sc->sc_intmask |= CM_RI;
600
601		/* this also clears the RI flag interupt: */
602		PUTREG(CMCMD, CM_RXBC(buffer));
603		PUTREG(CMSTAT, sc->sc_intmask);
604
605#ifdef CM_DEBUG
606		if_printf(ifp, "srint: restarted rx on buf %d\n", buffer);
607#endif
608	}
609}
610
611__inline static void
612cm_tint_locked(sc, isr)
613	struct cm_softc *sc;
614	int isr;
615{
616	struct ifnet *ifp;
617
618	int buffer;
619#ifdef CMTIMINGS
620	int clknow;
621#endif
622
623	ifp = sc->sc_ifp;
624	buffer = sc->sc_tx_act;
625
626	/*
627	 * retransmit code:
628	 * Normal situtations first for fast path:
629	 * If acknowledgement received ok or broadcast, we're ok.
630	 * else if
631	 */
632
633	if (isr & CM_TMA || sc->sc_broadcast[buffer])
634		ifp->if_opackets++;
635#ifdef CMRETRANSMIT
636	else if (ifp->if_flags & IFF_LINK2 && ifp->if_timer > 0
637	    && --sc->sc_retransmits[buffer] > 0) {
638		/* retransmit same buffer */
639		PUTREG(CMCMD, CM_TX(buffer));
640		return;
641	}
642#endif
643	else
644		ifp->if_oerrors++;
645
646
647	/* We know we can accept another buffer at this point. */
648	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
649
650	if (--sc->sc_tx_fillcount > 0) {
651
652		/*
653		 * start tx on other buffer.
654		 * This also clears the int flag
655		 */
656		buffer ^= 1;
657		sc->sc_tx_act = buffer;
658
659		/*
660		 * already given:
661		 * sc->sc_intmask |= CM_TA;
662		 * PUTREG(CMSTAT, sc->sc_intmask);
663		 */
664		PUTREG(CMCMD, CM_TX(buffer));
665		/* init watchdog timer */
666		ifp->if_timer = ARCTIMEOUT;
667
668#if defined(CM_DEBUG) && (CM_DEBUG > 1)
669		if_printf(ifp,
670		    "tint: starting tx on buffer %d, status 0x%02x\n",
671		    buffer, GETREG(CMSTAT));
672#endif
673	} else {
674		/* have to disable TX interrupt */
675		sc->sc_intmask &= ~CM_TA;
676		PUTREG(CMSTAT, sc->sc_intmask);
677		/* ... and watchdog timer */
678		ifp->if_timer = 0;
679
680#ifdef CM_DEBUG
681		if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n",
682		    GETREG(CMSTAT));
683#endif
684	}
685
686	/* XXXX TODO */
687#ifdef CMSOFTCOPY
688	/* schedule soft int to fill a new buffer for us */
689	softintr_schedule(sc->sc_txcookie);
690#else
691	/* call it directly */
692	cm_start_locked(ifp);
693#endif
694}
695
696/*
697 * Our interrupt routine
698 */
699void
700cmintr(arg)
701	void *arg;
702{
703	struct cm_softc *sc = arg;
704	struct ifnet *ifp = sc->sc_ifp;
705
706	u_char isr, maskedisr;
707	int buffer;
708	u_long newsec;
709
710	CM_LOCK(sc);
711
712	isr = GETREG(CMSTAT);
713	maskedisr = isr & sc->sc_intmask;
714	if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
715		CM_UNLOCK(sc);
716		return;
717	}
718
719	do {
720
721#if defined(CM_DEBUG) && (CM_DEBUG > 1)
722		if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n",
723		    isr, sc->sc_intmask);
724#endif
725
726		if (maskedisr & CM_POR) {
727			/*
728			 * XXX We should never see this. Don't bother to store
729			 * the address.
730			 * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF);
731			 */
732			PUTREG(CMCMD, CM_CLR(CLR_POR));
733			log(LOG_WARNING,
734			    "%s: intr: got spurious power on reset int\n",
735			    ifp->if_xname);
736		}
737
738		if (maskedisr & CM_RECON) {
739			/*
740			 * we dont need to:
741			 * PUTREG(CMCMD, CM_CONF(CONF_LONG));
742			 */
743			PUTREG(CMCMD, CM_CLR(CLR_RECONFIG));
744			ifp->if_collisions++;
745
746			/*
747			 * If less than 2 seconds per reconfig:
748			 *	If ARC_EXCESSIVE_RECONFIGS
749			 *	since last burst, complain and set treshold for
750			 *	warnings to ARC_EXCESSIVE_RECONS_REWARN.
751			 *
752			 * This allows for, e.g., new stations on the cable, or
753			 * cable switching as long as it is over after
754			 * (normally) 16 seconds.
755			 *
756			 * XXX TODO: check timeout bits in status word and
757			 * double time if necessary.
758			 */
759
760			callout_stop(&sc->sc_recon_ch);
761			newsec = time_second;
762			if ((newsec - sc->sc_recontime <= 2) &&
763			    (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) {
764				log(LOG_WARNING,
765				    "%s: excessive token losses, "
766				    "cable problem?\n",
767				    ifp->if_xname);
768			}
769			sc->sc_recontime = newsec;
770			callout_reset(&sc->sc_recon_ch, 15 * hz,
771			    cm_reconwatch_locked, (void *)sc);
772		}
773
774		if (maskedisr & CM_RI) {
775#if defined(CM_DEBUG) && (CM_DEBUG > 1)
776			if_printf(ifp, "intr: hard rint, act %d\n",
777			    sc->sc_rx_act);
778#endif
779
780			buffer = sc->sc_rx_act;
781			/* look if buffer is marked invalid: */
782			if (GETMEM(buffer * 512) == 0) {
783				/*
784				 * invalid marked buffer (or illegally
785				 * configured sender)
786				 */
787				log(LOG_WARNING,
788				    "%s: spurious RX interupt or sender 0 "
789				    " (ignored)\n", ifp->if_xname);
790				/*
791				 * restart receiver on same buffer.
792				 * XXX maybe better reset interface?
793				 */
794				PUTREG(CMCMD, CM_RXBC(buffer));
795			} else {
796				if (++sc->sc_rx_fillcount > 1) {
797					sc->sc_intmask &= ~CM_RI;
798					PUTREG(CMSTAT, sc->sc_intmask);
799				} else {
800					buffer ^= 1;
801					sc->sc_rx_act = buffer;
802
803					/*
804					 * Start receiver on other receive
805					 * buffer. This also clears the RI
806					 * interupt flag.
807					 */
808					PUTREG(CMCMD, CM_RXBC(buffer));
809					/* in RX intr, so mask is ok for RX */
810
811#ifdef CM_DEBUG
812					if_printf(ifp, "strt rx for buf %d, "
813					    "stat 0x%02x\n",
814					    sc->sc_rx_act, GETREG(CMSTAT));
815#endif
816				}
817
818#ifdef CMSOFTCOPY
819				/*
820				 * this one starts a soft int to copy out
821				 * of the hw
822				 */
823				softintr_schedule(sc->sc_rxcookie);
824#else
825				/* this one does the copy here */
826				cm_srint_locked(sc);
827#endif
828			}
829		}
830		if (maskedisr & CM_TA) {
831			cm_tint_locked(sc, isr);
832		}
833		isr = GETREG(CMSTAT);
834		maskedisr = isr & sc->sc_intmask;
835	} while (maskedisr);
836#if defined(CM_DEBUG) && (CM_DEBUG > 1)
837	if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n",
838	    isr, sc->sc_intmask);
839#endif
840	CM_UNLOCK(sc);
841}
842
843void
844cm_reconwatch_locked(arg)
845	void *arg;
846{
847	struct cm_softc *sc = arg;
848	struct ifnet *ifp = sc->sc_ifp;
849
850	if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) {
851		sc->sc_reconcount = 0;
852		log(LOG_WARNING, "%s: token valid again.\n",
853		    ifp->if_xname);
854	}
855	sc->sc_reconcount = 0;
856}
857
858
859/*
860 * Process an ioctl request.
861 * This code needs some work - it looks pretty ugly.
862 */
863int
864cm_ioctl(ifp, command, data)
865	struct ifnet *ifp;
866	u_long command;
867	caddr_t data;
868{
869	struct cm_softc *sc;
870	struct ifaddr *ifa;
871	struct ifreq *ifr;
872	int error;
873
874	error = 0;
875	sc = ifp->if_softc;
876	ifa = (struct ifaddr *)data;
877	ifr = (struct ifreq *)data;
878
879#if defined(CM_DEBUG) && (CM_DEBUG > 2)
880	if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command);
881#endif
882
883	switch (command) {
884	case SIOCSIFADDR:
885	case SIOCGIFADDR:
886	case SIOCADDMULTI:
887	case SIOCDELMULTI:
888	case SIOCSIFMTU:
889		error = arc_ioctl(ifp, command, data);
890		break;
891
892	case SIOCSIFFLAGS:
893		CM_LOCK(sc);
894		if ((ifp->if_flags & IFF_UP) == 0 &&
895		    (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
896			/*
897			 * If interface is marked down and it is running,
898			 * then stop it.
899			 */
900			cm_stop_locked(sc);
901			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
902		} else if ((ifp->if_flags & IFF_UP) != 0 &&
903			   (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
904			/*
905			 * If interface is marked up and it is stopped, then
906			 * start it.
907			 */
908			cm_init_locked(sc);
909		}
910		CM_UNLOCK(sc);
911		break;
912
913	default:
914		error = EINVAL;
915		break;
916	}
917
918	return (error);
919}
920
921/*
922 * watchdog routine for transmitter.
923 *
924 * We need this, because else a receiver whose hardware is alive, but whose
925 * software has not enabled the Receiver, would make our hardware wait forever
926 * Discovered this after 20 times reading the docs.
927 *
928 * Only thing we do is disable transmitter. We'll get a transmit timeout,
929 * and the int handler will have to decide not to retransmit (in case
930 * retransmission is implemented).
931 */
932void
933cm_watchdog(ifp)
934	struct ifnet *ifp;
935{
936	struct cm_softc *sc = ifp->if_softc;
937
938	CM_LOCK(sc);
939	PUTREG(CMCMD, CM_TXDIS);
940	CM_UNLOCK(sc);
941}
942