smc90cx6.c revision 271849
1255570Strasz/*	$NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */
2255570Strasz
3255570Strasz#include <sys/cdefs.h>
4255570Strasz__FBSDID("$FreeBSD: head/sys/dev/cm/smc90cx6.c 271849 2014-09-19 03:51:26Z glebius $");
5255570Strasz
6255570Strasz/*-
7255570Strasz * Copyright (c) 1994, 1995, 1998 The NetBSD Foundation, Inc.
8255570Strasz * All rights reserved.
9255570Strasz *
10255570Strasz * This code is derived from software contributed to The NetBSD Foundation
11255570Strasz * by Ignatios Souvatzis.
12255570Strasz *
13255570Strasz * Redistribution and use in source and binary forms, with or without
14255570Strasz * modification, are permitted provided that the following conditions
15255570Strasz * are met:
16255570Strasz * 1. Redistributions of source code must retain the above copyright
17255570Strasz *    notice, this list of conditions and the following disclaimer.
18255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
19255570Strasz *    notice, this list of conditions and the following disclaimer in the
20255570Strasz *    documentation and/or other materials provided with the distribution.
21255570Strasz *
22255570Strasz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23255570Strasz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24255570Strasz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25255570Strasz * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26255570Strasz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27255570Strasz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28255570Strasz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29255570Strasz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30255570Strasz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31270279Strasz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32270279Strasz * POSSIBILITY OF SUCH DAMAGE.
33270279Strasz */
34255570Strasz
35255570Strasz/*
36255570Strasz * Chip core driver for the SMC90c26 / SMC90c56 (and SMC90c66 in '56
37255570Strasz * compatibility mode) boards
38255570Strasz */
39255570Strasz
40267606Smav/* #define CMSOFTCOPY */
41267606Smav#define CMRETRANSMIT /**/
42255570Strasz/* #define CM_DEBUG */
43255570Strasz
44255570Strasz#include <sys/param.h>
45255570Strasz#include <sys/systm.h>
46255570Strasz#include <sys/sockio.h>
47255570Strasz#include <sys/mbuf.h>
48255570Strasz#include <sys/module.h>
49255570Strasz#include <sys/kernel.h>
50255570Strasz#include <sys/socket.h>
51255570Strasz#include <sys/syslog.h>
52255570Strasz#include <sys/bus.h>
53255570Strasz
54255570Strasz#include <machine/bus.h>
55255570Strasz#include <sys/rman.h>
56255570Strasz#include <machine/resource.h>
57255570Strasz
58255570Strasz#include <net/if.h>
59255570Strasz#include <net/if_var.h>
60255570Strasz#include <net/if_dl.h>
61255570Strasz#include <net/if_types.h>
62255570Strasz#include <net/if_arc.h>
63255570Strasz
64255570Strasz#include <dev/cm/smc90cx6reg.h>
65255570Strasz#include <dev/cm/smc90cx6var.h>
66255570Strasz
67255570StraszMODULE_DEPEND(if_cm, arcnet, 1, 1, 1);
68275864Smav
69255570Strasz/* these should be elsewhere */
70275864Smav
71255570Strasz#define ARC_MIN_LEN 1
72255570Strasz#define ARC_MIN_FORBID_LEN 254
73255570Strasz#define ARC_MAX_FORBID_LEN 256
74275864Smav#define ARC_MAX_LEN 508
75255570Strasz#define ARC_ADDR_LEN 1
76255570Strasz
77255570Strasz/* for watchdog timer. This should be more than enough. */
78255570Strasz#define ARCTIMEOUT (5*IFNET_SLOWHZ)
79255570Strasz
80255570Straszdevclass_t cm_devclass;
81255570Strasz
82255570Strasz/*
83255570Strasz * This currently uses 2 bufs for tx, 2 for rx
84255570Strasz *
85255570Strasz * New rx protocol:
86255570Strasz *
87255570Strasz * rx has a fillcount variable. If fillcount > (NRXBUF-1),
88255570Strasz * rx can be switched off from rx hard int.
89255570Strasz * Else rx is restarted on the other receiver.
90255570Strasz * rx soft int counts down. if it is == (NRXBUF-1), it restarts
91255570Strasz * the receiver.
92255570Strasz * To ensure packet ordering (we need that for 1201 later), we have a counter
93255570Strasz * which is incremented modulo 256 on each receive and a per buffer
94255570Strasz * variable, which is set to the counter on filling. The soft int can
95255570Strasz * compare both values to determine the older packet.
96255570Strasz *
97255570Strasz * Transmit direction:
98255570Strasz *
99255570Strasz * cm_start checks tx_fillcount
100255570Strasz * case 2: return
101255570Strasz *
102255570Strasz * else fill tx_act ^ 1 && inc tx_fillcount
103255570Strasz *
104255570Strasz * check tx_fillcount again.
105255570Strasz * case 2: set IFF_DRV_OACTIVE to stop arc_output from filling us.
106255570Strasz * case 1: start tx
107255570Strasz *
108255570Strasz * tint clears IFF_OACTIVE, decrements and checks tx_fillcount
109255570Strasz * case 1: start tx on tx_act ^ 1, softcall cm_start
110255570Strasz * case 0: softcall cm_start
111255570Strasz *
112255570Strasz * #define fill(i) get mbuf && copy mbuf to chip(i)
113255570Strasz */
114255570Strasz
115255570Straszvoid	cm_init(void *);
116255570Straszstatic void cm_init_locked(struct cm_softc *);
117255570Straszstatic void cm_reset_locked(struct cm_softc *);
118255570Straszvoid	cm_start(struct ifnet *);
119255570Straszvoid	cm_start_locked(struct ifnet *);
120255570Straszint	cm_ioctl(struct ifnet *, unsigned long, caddr_t);
121255570Straszvoid	cm_watchdog(void *);
122255570Straszvoid	cm_srint_locked(void *vsc);
123275864Smavstatic	void cm_tint_locked(struct cm_softc *, int);
124255570Straszvoid	cm_reconwatch_locked(void *);
125275864Smav
126255570Strasz/*
127255570Strasz * Release all resources
128255570Strasz */
129255570Straszvoid
130275864Smavcm_release_resources(dev)
131255570Strasz	device_t dev;
132255570Strasz{
133255570Strasz	struct cm_softc *sc = device_get_softc(dev);
134255570Strasz
135255570Strasz	if (sc->port_res != NULL) {
136255570Strasz		bus_release_resource(dev, SYS_RES_IOPORT,
137255570Strasz				     0, sc->port_res);
138255570Strasz		sc->port_res = NULL;
139255570Strasz	}
140255570Strasz	if (sc->mem_res != NULL) {
141255570Strasz		bus_release_resource(dev, SYS_RES_MEMORY,
142255570Strasz				     0, sc->mem_res);
143255570Strasz		sc->mem_res = NULL;
144255570Strasz	}
145255570Strasz	if (sc->irq_res != NULL) {
146255570Strasz		bus_release_resource(dev, SYS_RES_IRQ,
147255570Strasz				     0, sc->irq_res);
148255570Strasz		sc->irq_res = NULL;
149255570Strasz	}
150255570Strasz}
151255570Strasz
152255570Straszint
153255570Straszcm_attach(dev)
154255570Strasz	device_t dev;
155255570Strasz{
156255570Strasz	struct cm_softc *sc = device_get_softc(dev);
157255570Strasz	struct ifnet *ifp;
158255570Strasz	u_int8_t linkaddress;
159255570Strasz
160255570Strasz	ifp = sc->sc_ifp = if_alloc(IFT_ARCNET);
161255570Strasz	if (ifp == NULL)
162267606Smav		return (ENOSPC);
163273543Strasz
164267606Smav	/*
165267606Smav	 * read the arcnet address from the board
166267606Smav	 */
167267606Smav	GETREG(CMRESET);
168267606Smav	do {
169267606Smav		DELAY(200);
170267606Smav	} while (!(GETREG(CMSTAT) & CM_POR));
171267606Smav	linkaddress = GETMEM(CMMACOFF);
172267606Smav
173267606Smav	/* clear the int mask... */
174267606Smav	sc->sc_intmask = 0;
175267606Smav	PUTREG(CMSTAT, 0);
176267606Smav
177267606Smav	PUTREG(CMCMD, CM_CONF(CONF_LONG));
178267606Smav	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
179267606Smav	sc->sc_recontime = sc->sc_reconcount = 0;
180267606Smav
181267606Smav	/*
182267606Smav	 * set interface to stopped condition (reset)
183267606Smav	 */
184267606Smav	cm_stop_locked(sc);
185267606Smav
186267606Smav	ifp->if_softc = sc;
187267606Smav	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
188267606Smav	ifp->if_output = arc_output;
189267606Smav	ifp->if_start = cm_start;
190267606Smav	ifp->if_ioctl = cm_ioctl;
191267606Smav	ifp->if_init = cm_init;
192267606Smav	/* XXX IFQ_SET_READY(&ifp->if_snd); */
193267606Smav	ifp->if_snd.ifq_maxlen = ifqmaxlen;
194267606Smav	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
195267606Smav
196267606Smav	arc_ifattach(ifp, linkaddress);
197267606Smav
198267606Smav#ifdef CMSOFTCOPY
199267606Smav	sc->sc_rxcookie = softintr_establish(IPL_SOFTNET, cm_srint, sc);
200267606Smav	sc->sc_txcookie = softintr_establish(IPL_SOFTNET,
201267606Smav		(void (*)(void *))cm_start, ifp);
202267606Smav#endif
203267606Smav
204273813Strasz	callout_init_mtx(&sc->sc_recon_ch, &sc->sc_mtx, 0);
205273813Strasz	callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0);
206273813Strasz
207273813Strasz	if_printf(ifp, "link addr 0x%02x (%d)\n", linkaddress, linkaddress);
208273813Strasz	return 0;
209273813Strasz}
210273813Strasz
211273813Strasz/*
212273813Strasz * Initialize device
213273813Strasz *
214273813Strasz */
215273813Straszvoid
216273813Straszcm_init(xsc)
217273813Strasz	void *xsc;
218273813Strasz{
219273813Strasz	struct cm_softc *sc = (struct cm_softc *)xsc;
220273813Strasz
221273813Strasz	CM_LOCK(sc);
222273813Strasz	cm_init_locked(sc);
223273813Strasz	CM_UNLOCK(sc);
224273813Strasz}
225273813Strasz
226273813Straszstatic void
227273813Straszcm_init_locked(struct cm_softc *sc)
228273813Strasz{
229273813Strasz	struct ifnet *ifp = sc->sc_ifp;
230273813Strasz
231273813Strasz	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
232273813Strasz		ifp->if_drv_flags |= IFF_DRV_RUNNING;
233273813Strasz		cm_reset_locked(sc);
234273813Strasz	}
235273813Strasz}
236273813Strasz
237273813Strasz/*
238273813Strasz * Reset the interface...
239273813Strasz *
240273813Strasz * Assumes that it is called with sc_mtx held
241273813Strasz */
242273813Straszvoid
243273813Straszcm_reset_locked(sc)
244273813Strasz	struct cm_softc *sc;
245273813Strasz{
246273813Strasz	struct ifnet *ifp;
247273813Strasz	int linkaddress;
248273813Strasz
249273813Strasz	ifp = sc->sc_ifp;
250273813Strasz
251273813Strasz#ifdef CM_DEBUG
252273813Strasz	if_printf(ifp, "reset\n");
253273813Strasz#endif
254273813Strasz	/* stop and restart hardware */
255273813Strasz
256273813Strasz	GETREG(CMRESET);
257273813Strasz	do {
258273813Strasz		DELAY(200);
259273813Strasz	} while (!(GETREG(CMSTAT) & CM_POR));
260273813Strasz
261273813Strasz	linkaddress = GETMEM(CMMACOFF);
262273813Strasz
263255570Strasz#if defined(CM_DEBUG) && (CM_DEBUG > 2)
264255570Strasz	if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n",
265255570Strasz	    linkaddress, linkaddress);
266255570Strasz#endif
267255570Strasz
268273543Strasz	/* tell the routing level about the (possibly changed) link address */
269273543Strasz	arc_storelladdr(ifp, linkaddress);
270255570Strasz	arc_frag_init(ifp);
271255570Strasz
272273543Strasz	/* POR is NMI, but we need it below: */
273273543Strasz	sc->sc_intmask = CM_RECON|CM_POR;
274255570Strasz	PUTREG(CMSTAT, sc->sc_intmask);
275255570Strasz	PUTREG(CMCMD, CM_CONF(CONF_LONG));
276255570Strasz
277255570Strasz#ifdef CM_DEBUG
278255570Strasz	if_printf(ifp, "reset: chip configured, status=0x%02x\n",
279255570Strasz	    GETREG(CMSTAT));
280255570Strasz#endif
281255570Strasz	PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
282255570Strasz
283255570Strasz#ifdef CM_DEBUG
284255570Strasz	if_printf(ifp, "reset: bits cleared, status=0x%02x\n",
285255570Strasz	     GETREG(CMSTAT));
286255570Strasz#endif
287273543Strasz
288273543Strasz	sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS;
289255570Strasz
290255570Strasz	/* start receiver */
291261757Strasz
292255570Strasz	sc->sc_intmask  |= CM_RI;
293255570Strasz	sc->sc_rx_fillcount = 0;
294273813Strasz	sc->sc_rx_act = 2;
295273813Strasz
296273813Strasz	PUTREG(CMCMD, CM_RXBC(2));
297273813Strasz	PUTREG(CMSTAT, sc->sc_intmask);
298267606Smav
299255570Strasz#ifdef CM_DEBUG
300255570Strasz	if_printf(ifp, "reset: started receiver, status=0x%02x\n",
301273543Strasz	    GETREG(CMSTAT));
302255570Strasz#endif
303255570Strasz
304255570Strasz	/* and init transmitter status */
305273813Strasz	sc->sc_tx_act = 0;
306273813Strasz	sc->sc_tx_fillcount = 0;
307273813Strasz
308273813Strasz	ifp->if_drv_flags |= IFF_DRV_RUNNING;
309273813Strasz	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
310273813Strasz
311273813Strasz	callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc);
312255570Strasz	cm_start_locked(ifp);
313255570Strasz}
314255570Strasz
315255570Strasz/*
316255570Strasz * Take interface offline
317255570Strasz */
318255570Straszvoid
319255570Straszcm_stop_locked(sc)
320255570Strasz	struct cm_softc *sc;
321255570Strasz{
322255570Strasz	/* Stop the interrupts */
323255570Strasz	PUTREG(CMSTAT, 0);
324255570Strasz
325255570Strasz	/* Stop the interface */
326255570Strasz	GETREG(CMRESET);
327255570Strasz
328255570Strasz	/* Stop watchdog timer */
329255570Strasz	callout_stop(&sc->sc_watchdog_timer);
330255570Strasz	sc->sc_timer = 0;
331}
332
333void
334cm_start(struct ifnet *ifp)
335{
336	struct cm_softc *sc = ifp->if_softc;
337
338	CM_LOCK(sc);
339	cm_start_locked(ifp);
340	CM_UNLOCK(sc);
341}
342
343/*
344 * Start output on interface. Get another datagram to send
345 * off the interface queue, and copy it to the
346 * interface becore starting the output
347 *
348 * Assumes that sc_mtx is held
349 */
350void
351cm_start_locked(ifp)
352	struct ifnet *ifp;
353{
354	struct cm_softc *sc = ifp->if_softc;
355	struct mbuf *m, *mp;
356
357	int cm_ram_ptr;
358	int len, tlen, offset, buffer;
359#ifdef CMTIMINGS
360	u_long copystart, lencopy, perbyte;
361#endif
362
363#if defined(CM_DEBUG) && (CM_DEBUG > 3)
364	if_printf(ifp, "start(%p)\n", ifp);
365#endif
366
367	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
368		return;
369
370	if (sc->sc_tx_fillcount >= 2)
371		return;
372
373	m = arc_frag_next(ifp);
374	buffer = sc->sc_tx_act ^ 1;
375
376	if (m == 0)
377		return;
378
379#ifdef CM_DEBUG
380	if (m->m_len < ARC_HDRLEN)
381		m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */
382	if_printf(ifp, "start: filling %d from %d to %d type %d\n",
383	    buffer, mtod(m, u_char *)[0],
384	    mtod(m, u_char *)[1], mtod(m, u_char *)[2]);
385#else
386	if (m->m_len < 2)
387		m = m_pullup(m, 2);
388#endif
389	cm_ram_ptr = buffer * 512;
390
391	if (m == 0)
392		return;
393
394	/* write the addresses to RAM and throw them away */
395
396	/*
397	 * Hardware does this: Yet Another Microsecond Saved.
398	 * (btw, timing code says usually 2 microseconds)
399	 * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]);
400	 */
401
402	PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]);
403	m_adj(m, 2);
404
405	/* get total length left at this point */
406	tlen = m->m_pkthdr.len;
407	if (tlen < ARC_MIN_FORBID_LEN) {
408		offset = 256 - tlen;
409		PUTMEM(cm_ram_ptr + 2, offset);
410	} else {
411		PUTMEM(cm_ram_ptr + 2, 0);
412		if (tlen <= ARC_MAX_FORBID_LEN)
413			offset = 255;		/* !!! */
414		else {
415			if (tlen > ARC_MAX_LEN)
416				tlen = ARC_MAX_LEN;
417			offset = 512 - tlen;
418		}
419		PUTMEM(cm_ram_ptr + 3, offset);
420
421	}
422	cm_ram_ptr += offset;
423
424	/* lets loop through the mbuf chain */
425
426	for (mp = m; mp; mp = mp->m_next) {
427		if ((len = mp->m_len)) {		/* YAMS */
428			bus_space_write_region_1(
429			    rman_get_bustag(sc->mem_res),
430			    rman_get_bushandle(sc->mem_res),
431			    cm_ram_ptr, mtod(mp, caddr_t), len);
432
433			cm_ram_ptr += len;
434		}
435	}
436
437	sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0;
438	sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5;
439
440	if (++sc->sc_tx_fillcount > 1) {
441		/*
442		 * We are filled up to the rim. No more bufs for the moment,
443		 * please.
444		 */
445		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
446	} else {
447#ifdef CM_DEBUG
448		if_printf(ifp, "start: starting transmitter on buffer %d\n",
449		    buffer);
450#endif
451		/* Transmitter was off, start it */
452		sc->sc_tx_act = buffer;
453
454		/*
455		 * We still can accept another buf, so don't:
456		 * ifp->if_drv_flags |= IFF_DRV_OACTIVE;
457		 */
458		sc->sc_intmask |= CM_TA;
459		PUTREG(CMCMD, CM_TX(buffer));
460		PUTREG(CMSTAT, sc->sc_intmask);
461
462		sc->sc_timer = ARCTIMEOUT;
463	}
464	m_freem(m);
465
466	/*
467	 * After 10 times reading the docs, I realized
468	 * that in the case the receiver NAKs the buffer request,
469	 * the hardware retries till shutdown.
470	 * This is integrated now in the code above.
471	 */
472}
473
474#ifdef CMSOFTCOPY
475void
476cm_srint(void *vsc)
477{
478	struct cm_softc *sc = (struct cm_softc *)vsc;
479
480	CM_LOCK(sc);
481	cm_srint_locked(vsc);
482	CM_UNLOCK(sc);
483}
484#endif
485
486/*
487 * Arcnet interface receiver soft interrupt:
488 * get the stuff out of any filled buffer we find.
489 */
490void
491cm_srint_locked(vsc)
492	void *vsc;
493{
494	struct cm_softc *sc = (struct cm_softc *)vsc;
495	int buffer, len, offset, type;
496	int cm_ram_ptr;
497	struct mbuf *m;
498	struct arc_header *ah;
499	struct ifnet *ifp;
500
501	ifp = sc->sc_ifp;
502
503	buffer = sc->sc_rx_act ^ 1;
504
505	/* Allocate header mbuf */
506	MGETHDR(m, M_NOWAIT, MT_DATA);
507
508	if (m == 0) {
509		/*
510		 * in case s.th. goes wrong with mem, drop it
511		 * to make sure the receiver can be started again
512		 * count it as input error (we dont have any other
513		 * detectable)
514		 */
515		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
516		goto cleanup;
517	}
518
519	m->m_pkthdr.rcvif = ifp;
520
521	/*
522	 * Align so that IP packet will be longword aligned. Here we
523	 * assume that m_data of new packet is longword aligned.
524	 * When implementing PHDS, we might have to change it to 2,
525	 * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent.
526	 */
527
528	cm_ram_ptr = buffer * 512;
529	offset = GETMEM(cm_ram_ptr + 2);
530	if (offset)
531		len = 256 - offset;
532	else {
533		offset = GETMEM(cm_ram_ptr + 3);
534		len = 512 - offset;
535	}
536
537	/*
538	 * first +2 bytes for align fixup below
539	 * second +2 bytes are for src/dst addresses
540	 */
541	if ((len + 2 + 2) > MHLEN) {
542		/* attach an mbuf cluster */
543		MCLGET(m, M_NOWAIT);
544
545		/* Insist on getting a cluster */
546		if ((m->m_flags & M_EXT) == 0) {
547			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
548			goto cleanup;
549		}
550	}
551
552	if (m == 0) {
553		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
554		goto cleanup;
555	}
556
557	type = GETMEM(cm_ram_ptr + offset);
558	m->m_data += 1 + arc_isphds(type);
559	/* mbuf filled with ARCnet addresses */
560	m->m_pkthdr.len = m->m_len = len + 2;
561
562	ah = mtod(m, struct arc_header *);
563	ah->arc_shost = GETMEM(cm_ram_ptr + 0);
564	ah->arc_dhost = GETMEM(cm_ram_ptr + 1);
565
566	bus_space_read_region_1(
567	    rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res),
568	    cm_ram_ptr + offset, mtod(m, u_char *) + 2, len);
569
570	CM_UNLOCK(sc);
571	arc_input(ifp, m);
572	CM_LOCK(sc);
573
574	m = NULL;
575	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
576
577cleanup:
578
579	if (m != NULL)
580		m_freem(m);
581
582	/* mark buffer as invalid by source id 0 */
583	PUTMEM(buffer << 9, 0);
584	if (--sc->sc_rx_fillcount == 2 - 1) {
585
586		/* was off, restart it on buffer just emptied */
587		sc->sc_rx_act = buffer;
588		sc->sc_intmask |= CM_RI;
589
590		/* this also clears the RI flag interrupt: */
591		PUTREG(CMCMD, CM_RXBC(buffer));
592		PUTREG(CMSTAT, sc->sc_intmask);
593
594#ifdef CM_DEBUG
595		if_printf(ifp, "srint: restarted rx on buf %d\n", buffer);
596#endif
597	}
598}
599
600static inline void
601cm_tint_locked(sc, isr)
602	struct cm_softc *sc;
603	int isr;
604{
605	struct ifnet *ifp;
606
607	int buffer;
608#ifdef CMTIMINGS
609	int clknow;
610#endif
611
612	ifp = sc->sc_ifp;
613	buffer = sc->sc_tx_act;
614
615	/*
616	 * retransmit code:
617	 * Normal situtations first for fast path:
618	 * If acknowledgement received ok or broadcast, we're ok.
619	 * else if
620	 */
621
622	if (isr & CM_TMA || sc->sc_broadcast[buffer])
623		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
624#ifdef CMRETRANSMIT
625	else if (ifp->if_flags & IFF_LINK2 && sc->sc_timer > 0
626	    && --sc->sc_retransmits[buffer] > 0) {
627		/* retransmit same buffer */
628		PUTREG(CMCMD, CM_TX(buffer));
629		return;
630	}
631#endif
632	else
633		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
634
635
636	/* We know we can accept another buffer at this point. */
637	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
638
639	if (--sc->sc_tx_fillcount > 0) {
640
641		/*
642		 * start tx on other buffer.
643		 * This also clears the int flag
644		 */
645		buffer ^= 1;
646		sc->sc_tx_act = buffer;
647
648		/*
649		 * already given:
650		 * sc->sc_intmask |= CM_TA;
651		 * PUTREG(CMSTAT, sc->sc_intmask);
652		 */
653		PUTREG(CMCMD, CM_TX(buffer));
654		/* init watchdog timer */
655		sc->sc_timer = ARCTIMEOUT;
656
657#if defined(CM_DEBUG) && (CM_DEBUG > 1)
658		if_printf(ifp,
659		    "tint: starting tx on buffer %d, status 0x%02x\n",
660		    buffer, GETREG(CMSTAT));
661#endif
662	} else {
663		/* have to disable TX interrupt */
664		sc->sc_intmask &= ~CM_TA;
665		PUTREG(CMSTAT, sc->sc_intmask);
666		/* ... and watchdog timer */
667		sc->sc_timer = 0;
668
669#ifdef CM_DEBUG
670		if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n",
671		    GETREG(CMSTAT));
672#endif
673	}
674
675	/* XXXX TODO */
676#ifdef CMSOFTCOPY
677	/* schedule soft int to fill a new buffer for us */
678	softintr_schedule(sc->sc_txcookie);
679#else
680	/* call it directly */
681	cm_start_locked(ifp);
682#endif
683}
684
685/*
686 * Our interrupt routine
687 */
688void
689cmintr(arg)
690	void *arg;
691{
692	struct cm_softc *sc = arg;
693	struct ifnet *ifp = sc->sc_ifp;
694
695	u_char isr, maskedisr;
696	int buffer;
697	u_long newsec;
698
699	CM_LOCK(sc);
700
701	isr = GETREG(CMSTAT);
702	maskedisr = isr & sc->sc_intmask;
703	if (!maskedisr || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
704		CM_UNLOCK(sc);
705		return;
706	}
707
708	do {
709
710#if defined(CM_DEBUG) && (CM_DEBUG > 1)
711		if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n",
712		    isr, sc->sc_intmask);
713#endif
714
715		if (maskedisr & CM_POR) {
716			/*
717			 * XXX We should never see this. Don't bother to store
718			 * the address.
719			 * sc->sc_ifp->if_l2com->ac_anaddr = GETMEM(CMMACOFF);
720			 */
721			PUTREG(CMCMD, CM_CLR(CLR_POR));
722			log(LOG_WARNING,
723			    "%s: intr: got spurious power on reset int\n",
724			    ifp->if_xname);
725		}
726
727		if (maskedisr & CM_RECON) {
728			/*
729			 * we dont need to:
730			 * PUTREG(CMCMD, CM_CONF(CONF_LONG));
731			 */
732			PUTREG(CMCMD, CM_CLR(CLR_RECONFIG));
733			if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
734
735			/*
736			 * If less than 2 seconds per reconfig:
737			 *	If ARC_EXCESSIVE_RECONFIGS
738			 *	since last burst, complain and set treshold for
739			 *	warnings to ARC_EXCESSIVE_RECONS_REWARN.
740			 *
741			 * This allows for, e.g., new stations on the cable, or
742			 * cable switching as long as it is over after
743			 * (normally) 16 seconds.
744			 *
745			 * XXX TODO: check timeout bits in status word and
746			 * double time if necessary.
747			 */
748
749			callout_stop(&sc->sc_recon_ch);
750			newsec = time_second;
751			if ((newsec - sc->sc_recontime <= 2) &&
752			    (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) {
753				log(LOG_WARNING,
754				    "%s: excessive token losses, "
755				    "cable problem?\n",
756				    ifp->if_xname);
757			}
758			sc->sc_recontime = newsec;
759			callout_reset(&sc->sc_recon_ch, 15 * hz,
760			    cm_reconwatch_locked, (void *)sc);
761		}
762
763		if (maskedisr & CM_RI) {
764#if defined(CM_DEBUG) && (CM_DEBUG > 1)
765			if_printf(ifp, "intr: hard rint, act %d\n",
766			    sc->sc_rx_act);
767#endif
768
769			buffer = sc->sc_rx_act;
770			/* look if buffer is marked invalid: */
771			if (GETMEM(buffer * 512) == 0) {
772				/*
773				 * invalid marked buffer (or illegally
774				 * configured sender)
775				 */
776				log(LOG_WARNING,
777				    "%s: spurious RX interrupt or sender 0 "
778				    " (ignored)\n", ifp->if_xname);
779				/*
780				 * restart receiver on same buffer.
781				 * XXX maybe better reset interface?
782				 */
783				PUTREG(CMCMD, CM_RXBC(buffer));
784			} else {
785				if (++sc->sc_rx_fillcount > 1) {
786					sc->sc_intmask &= ~CM_RI;
787					PUTREG(CMSTAT, sc->sc_intmask);
788				} else {
789					buffer ^= 1;
790					sc->sc_rx_act = buffer;
791
792					/*
793					 * Start receiver on other receive
794					 * buffer. This also clears the RI
795					 * interrupt flag.
796					 */
797					PUTREG(CMCMD, CM_RXBC(buffer));
798					/* in RX intr, so mask is ok for RX */
799
800#ifdef CM_DEBUG
801					if_printf(ifp, "strt rx for buf %d, "
802					    "stat 0x%02x\n",
803					    sc->sc_rx_act, GETREG(CMSTAT));
804#endif
805				}
806
807#ifdef CMSOFTCOPY
808				/*
809				 * this one starts a soft int to copy out
810				 * of the hw
811				 */
812				softintr_schedule(sc->sc_rxcookie);
813#else
814				/* this one does the copy here */
815				cm_srint_locked(sc);
816#endif
817			}
818		}
819		if (maskedisr & CM_TA) {
820			cm_tint_locked(sc, isr);
821		}
822		isr = GETREG(CMSTAT);
823		maskedisr = isr & sc->sc_intmask;
824	} while (maskedisr);
825#if defined(CM_DEBUG) && (CM_DEBUG > 1)
826	if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n",
827	    isr, sc->sc_intmask);
828#endif
829	CM_UNLOCK(sc);
830}
831
832void
833cm_reconwatch_locked(arg)
834	void *arg;
835{
836	struct cm_softc *sc = arg;
837	struct ifnet *ifp = sc->sc_ifp;
838
839	if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) {
840		sc->sc_reconcount = 0;
841		log(LOG_WARNING, "%s: token valid again.\n",
842		    ifp->if_xname);
843	}
844	sc->sc_reconcount = 0;
845}
846
847
848/*
849 * Process an ioctl request.
850 * This code needs some work - it looks pretty ugly.
851 */
852int
853cm_ioctl(ifp, command, data)
854	struct ifnet *ifp;
855	u_long command;
856	caddr_t data;
857{
858	struct cm_softc *sc;
859	int error;
860
861	error = 0;
862	sc = ifp->if_softc;
863
864#if defined(CM_DEBUG) && (CM_DEBUG > 2)
865	if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command);
866#endif
867
868	switch (command) {
869	case SIOCSIFADDR:
870	case SIOCGIFADDR:
871	case SIOCADDMULTI:
872	case SIOCDELMULTI:
873	case SIOCSIFMTU:
874		error = arc_ioctl(ifp, command, data);
875		break;
876
877	case SIOCSIFFLAGS:
878		CM_LOCK(sc);
879		if ((ifp->if_flags & IFF_UP) == 0 &&
880		    (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
881			/*
882			 * If interface is marked down and it is running,
883			 * then stop it.
884			 */
885			cm_stop_locked(sc);
886			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
887		} else if ((ifp->if_flags & IFF_UP) != 0 &&
888			   (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
889			/*
890			 * If interface is marked up and it is stopped, then
891			 * start it.
892			 */
893			cm_init_locked(sc);
894		}
895		CM_UNLOCK(sc);
896		break;
897
898	default:
899		error = EINVAL;
900		break;
901	}
902
903	return (error);
904}
905
906/*
907 * watchdog routine for transmitter.
908 *
909 * We need this, because else a receiver whose hardware is alive, but whose
910 * software has not enabled the Receiver, would make our hardware wait forever
911 * Discovered this after 20 times reading the docs.
912 *
913 * Only thing we do is disable transmitter. We'll get a transmit timeout,
914 * and the int handler will have to decide not to retransmit (in case
915 * retransmission is implemented).
916 */
917void
918cm_watchdog(void *arg)
919{
920	struct cm_softc *sc;
921
922	sc = arg;
923	callout_reset(&sc->sc_watchdog_timer, hz, cm_watchdog, sc);
924	if (sc->sc_timer == 0 || --sc->sc_timer > 0)
925		return;
926	PUTREG(CMCMD, CM_TXDIS);
927}
928