xref: /original-bsd/sys/i386/isa/if_apx.c (revision 54083e20)
1 /*
2  * Copyright (c) 1982, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)if_apx.c	8.1 (Berkeley) 06/11/93
8  */
9 
10 /*
11  * Driver for SGS-THOMSON MK5025 based Link level controller.
12  * The chip will do LAPB in hardware, although this driver only
13  * attempts to use it for HDLC framing.
14  *
15  * Driver written by Keith Sklower, based on lance AMD7990
16  * driver by Van Jacobsen, and information graciously supplied
17  * by the ADAX corporation of Berkeley, CA.
18  */
19 
20 #include "apx.h"
21 #if NAPX > 0
22 
23 #include <sys/param.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/errno.h>
28 #include <sys/syslog.h>
29 
30 #include <net/if.h>
31 #include <net/netisr.h>
32 #include <net/if_types.h>
33 #ifdef CCITT
34 #include <netccitt/x25.h>
35 int x25_rtrequest(), x25_ifoutput();
36 #endif
37 
38 #include <i386/isa/if_apxreg.h>
39 
40 int	apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
41 int	apxinit(), apxoutput(), apxioctl(), apxreset(), apxdebug = 0;
42 void	apx_ifattach(), apxtest(), apxinput(), apxintr(), apxtint(), apxrint();
43 
44 struct apx_softc {
45 	struct	ifnet apx_if;
46 	caddr_t	apx_device;		/* e.g. isa_device, vme_device, etc. */
47 	struct	apc_reg *apx_reg;	/* control regs for both subunits */
48 	struct	apc_mem *apx_hmem;	/* Host addr for shared memory */
49 	struct	apc_mem *apx_dmem;	/* Device (chip) addr for shared mem */
50 	struct	sgcp *apx_sgcp;		/* IO control port for this subunit */
51 	int	apx_flags;		/* Flags specific to this driver */
52 #define APXF_CHIPHERE	0x01		/* mk5025 present */
53 	int	apx_rxnum;		/* Last receiver dx we looked at */
54 	int	apx_txnum;		/* Last tranmistter dx we stomped on */
55 	int	apx_txcnt;		/* Number of packets queued for tx*/
56 	u_int	apx_msize;
57 	struct	sgae apx_csr23;		/* 24 bit init addr, as seen by chip */
58 	u_short	apx_csr4;		/* byte gender, set in mach dep code */
59 	struct	apc_modes apx_modes;	/* Parameters, as amended by ioctls */
60 } apx_softc[2 * NAPX];
61 
62 struct apxstat {
63 	int	rxnull;			/* no rx bufs ready this interrupt */
64 	int	rxnrdy;			/* expected rx buf not ready */
65 	int	rx2big;			/* expected rx buf not ready */
66 	int	txnull;
67 	int	pint;			/* new primitive available interrupt */
68 	int	rint;			/* receive interrupts */
69 	int	tint;			/* transmit interrupts */
70 	int	anyint;			/* note all interrupts */
71 	int	queued;			/* got through apxinput */
72 	int	nxpctd;			/* received while if was down */
73 	int	rstfld;			/* reset didn't work */
74 } apxstat;
75 
76 /* default operating paramters for devices */
77 struct	apc_modes apx_default_modes = {
78  { 1,		/* apm_sgob.lsaddr; */
79    3,		/* apm_sgob.rsaddr; */
80    -SGMTU,	/* apm_sgob.n1; */
81    ((-10)<<8),	/* apm_sgob.n2_scale; */
82    -1250,	/* apm_sgob.t1; */
83    -10000,	/* apm_sgob.t3; */
84    -80,		/* apm_sgob.tp; */
85  },
86  2,		/* apm_txwin; */
87  1,		/* apm_apxmode: RS_232 connector and modem clock; */
88  0,		/* apm_apxaltmode: enable dtr, disable X.21 connector; */
89  IFT_X25,	/* apm_iftype; */
90 };
91 
92 /* Begin bus & endian dependence */
93 
94 #include <i386/isa/isa_device.h>
95 
96 struct	isa_driver apxdriver = {
97 	apxprobe, apxattach, "apx",
98 };
99 
100 #define SG_RCSR(apx, csrnum) \
101 	 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
102 	  inw(&(apx->apx_sgcp->sgcp_rdp)))
103 
104 #define SG_WCSR(apx, csrnum, data) \
105 	 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
106 	  outw(&(apx->apx_sgcp->sgcp_rdp), data))
107 
108 #define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname))
109 #define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data)
110 
111 #define TIMO 10000 /* used in apx_uprim */
112 
113 apxprobe(id)
114 	register struct	isa_device *id;
115 {
116 	int	moffset = 0, nchips = 2, unit = id->id_unit << 1, subunit;
117 	struct	apc_reg *reg = (struct apc_reg *)id->id_iobase;
118 	register struct	apx_softc *apx = apx_softc + unit;
119 
120 	/*
121 	 * Probing for the second MK5025 on all ISA/EISA adax boards
122 	 * manufactured prior to July 1992 (and some time following)
123 	 * will hang the bus and the system.  Thus, it is essential
124 	 * not to probe for the second mk5025 if it is known not to be there.
125 	 * As the current config scheme for 386BSD does not have a flags
126 	 * field, we adopt the convention of using the low order bit of
127 	 * the memsize to warn us that we have a single chip board.
128 	 */
129 	if (id->id_msize & 1)
130 		nchips = 1;
131 	for (subunit = 0; subunit < nchips; subunit++) {
132 		apx->apx_msize	= id->id_msize >> 1;
133 		apx->apx_hmem	= (struct apc_mem *) (id->id_maddr + moffset);
134 		apx->apx_dmem	= (struct apc_mem *) moffset;
135 		apx->apx_device	= (caddr_t) id;
136 		apx->apx_reg	= reg;
137 		apx->apx_sgcp	= reg->axr_sgcp + subunit;
138 		apx->apx_csr4	= 0x0210;	/* no byte swapping for PC-AT */
139 		apx->apx_modes	= apx_default_modes;
140 		apx->apx_if.if_unit = unit++;
141 		moffset = apx->apx_msize;
142 		apxtest(apx++);
143 	}
144 	return 1;
145 }
146 
147 apxattach(id)
148 	struct	isa_device *id;
149 {
150 	register struct	apx_softc *apx = apx_softc + (id->id_unit << 1);
151 
152 	apx_ifattach(&((apx++)->apx_if));
153 	apx_ifattach(&(apx->apx_if));
154 	return 0;
155 }
156 /* End bus & endian dependence */
157 
158 /*
159  * Interface exists: make available by filling in network interface
160  * record.  System will initialize the interface when it is ready
161  * to accept packets.
162  */
163 void
164 apx_ifattach(ifp)
165 	register struct ifnet *ifp;
166 {
167 	/*
168 	 * Initialize ifnet structure
169 	 */
170 	ifp->if_name	= "apc";
171 	ifp->if_mtu	= SGMTU;
172 	ifp->if_init	= apxinit;
173 	ifp->if_output	= apxoutput;
174 	ifp->if_start	= apxstart;
175 	ifp->if_ioctl	= apxioctl;
176 	ifp->if_reset	= apxreset;
177 	ifp->if_type	= apx_default_modes.apm_iftype;
178 	ifp->if_hdrlen	= 5;
179 	ifp->if_addrlen	= 8;
180 	if_attach(ifp);
181 }
182 /*
183  * Initialization of interface
184  */
185 apxinit(unit)
186 	int unit;
187 {
188 	struct ifnet *ifp = &apx_softc[unit].apx_if;
189 	int s = splimp();
190 
191 	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
192 	if (apxreset(unit) && (ifp->if_flags & IFF_UP)) {
193 		ifp->if_flags |= IFF_RUNNING;
194 		(void)apxstart(ifp);
195 	}
196 	splx(s);
197 	return 0;
198 }
199 
200 apxctr(apx)
201 	register struct apx_softc *apx;
202 {
203 	APX_WCSR(apx, axr_ccr, 0xB0); /* select ctr 2, write lsb+msb, mode 0 */
204 	APX_WCSR(apx, axr_cnt2, 0x1);
205 	APX_WCSR(apx, axr_cnt2, 0x0);
206 	DELAY(50);
207 	APX_WCSR(apx, axr_ccr, 0xE8); /* latch status, ctr 2; */
208 	return (APX_RCSR(apx, axr_cnt2));
209 }
210 
211 void
212 apxtest(apx)
213 	register struct apx_softc *apx;
214 {
215 	int i =  0;
216 
217 	if ((apx->apx_if.if_unit & 1) == 0 && (i = apxctr(apx)) == 0)
218 		apxerror(apx, "no response from timer chip", 0);
219 	if (SG_RCSR(apx, 1) & 0x8000)
220 		SG_WCSR(apx, 1, 0x8040);
221 	SG_WCSR(apx, 4, apx->apx_csr4);
222 	SG_WCSR(apx, 5, 0x08);		/* Set DTR mode in SGS thompson chip */
223 	if (((i = SG_RCSR(apx, 5)) & 0xff08) != 0x08)
224 		apxerror(apx, "no mk5025, csr5 high bits are", i);
225 	else
226 		apx->apx_flags |= APXF_CHIPHERE;
227 	(void) apx_uprim(apx, SG_STOP, "stop after probing");
228 }
229 
230 apxreset(unit)
231 	int	unit;
232 {
233 	register struct apx_softc *apx = &apx_softc[unit ^ 1];
234 	u_char apm_apxmode = 0, apm_apxaltmode = 0;
235 #define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
236 
237 	MODE(apm_apxmode);
238 	MODE(apm_apxaltmode);
239 	apx = apx_softc + unit;
240 	MODE(apm_apxmode);
241 	MODE(apm_apxaltmode);
242 	APX_WCSR(apx, axr_mode, apm_apxmode);
243 	APX_WCSR(apx, axr_altmode, apm_apxaltmode);
244 	(void) apxctr(apx);
245 	(void) apx_uprim(apx, SG_STOP, "stop to reset");
246 	if ((apx->apx_if.if_flags & IFF_UP) == 0)
247 		return 0;
248 	apx_meminit(apx->apx_hmem, apx);
249 	SG_WCSR(apx, 4, apx->apx_csr4);
250 	SG_WCSR(apx, 2, apx->apx_csr23.f_hi);
251 	SG_WCSR(apx, 3, apx->apx_csr23.lo);
252 	if (apx_uprim(apx, SG_INIT, "init request") ||
253 	    apx_uprim(apx, SG_STAT, "status request") ||
254 	    apx_uprim(apx, SG_TRANS, "transparent mode"))
255 		return 0;
256 	SG_WCSR(apx, 0, SG_INEA);
257 	return 1;
258 }
259 
260 apx_uprim(apx, request, ident)
261 	register struct apx_softc *apx;
262 	char *ident;
263 {
264 	register int timo = 0;
265 	int reply;
266 
267 	if ((apx->apx_flags & APXF_CHIPHERE) == 0)
268 		return 1;	/* maybe even should panic . . . */
269 	if ((reply = SG_RCSR(apx, 1)) & 0x8040)
270 		SG_WCSR(apx, 1, 0x8040); /* Magic! */
271 	if (request == SG_STOP && (SG_RCSR(apx, 0) & SG_STOPPED))
272 		return 0;
273 	SG_WCSR(apx, 1, request | SG_UAV);
274 	do {
275 		reply = SG_RCSR(apx, 1);
276 		if (timo++ >= TIMO || (reply & 0x8000)) {
277 				apxerror(apx, ident, reply);
278 				return 1;
279 		}
280 	} while (reply & SG_UAV);
281 	return 0;
282 }
283 
284 apx_meminit(apc, apx)
285 	register struct apc_mem *apc;
286 	struct apx_softc *apx;
287 {
288 	register struct apc_mem *apcbase = apx->apx_dmem;
289 	register int i;
290 #define LOWADDR(e) (((u_long)&(apcbase->e)) & 0xffff)
291 #define HIADDR(e) ((((u_long)&(apcbase->e)) >> 16) & 0xff)
292 #define SET_SGAE(d, f, a) {(d).lo = LOWADDR(a); (d).f_hi = (f) | HIADDR(a);}
293 #define SET_SGDX(d, f, a, b) \
294 	{SET_SGAE((d).sgdx_ae, f, a); (d).sgdx_mcnt = (d).sgdx_bcnt = (b);}
295 
296 	apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = 0;
297 	bzero((caddr_t)apc, ((caddr_t)(&apc->apc_rxmd[0])) - (caddr_t)apc);
298 	apc->apc_mode = 0x0108;	/* 2 flag spacing, leave addr & ctl, do CRC16 */
299 	apc->apc_sgop = apx->apx_modes.apm_sgop;
300 	SET_SGAE(apx->apx_csr23, SG_UIE | SG_PROM, apc_mode);
301 	SET_SGAE(apc->apc_rxdd, SG_RLEN, apc_rxmd[0]);
302 	i = SG_TLEN | ((apx->apx_modes.apm_txwin)<< 8);
303 	SET_SGAE(apc->apc_txdd, i, apc_txmd[0]);
304 	SET_SGAE(apc->apc_stdd, 0, apc_sgsb);
305 	SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf[0], -SGMTU);
306 	SET_SGDX(apc->apc_txtid, 0, apc_txidbuf[0], 0);
307 	for (i = 0; i < SGRBUF; i++)
308 		 SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU)
309 	for (i = 0; i < SGTBUF; i++)
310 		 SET_SGDX(apc->apc_txmd[i], 0, apc_tbuf[i][0], 0)
311 }
312 
313 /*
314  * Start output on interface.  Get another datagram to send
315  * off of the interface queue, and copy it to the interface
316  * before starting the output.
317  */
318 apxstart(ifp)
319 	struct ifnet *ifp;
320 {
321 	register struct apx_softc *apx = &apx_softc[ifp->if_unit];
322 	register struct sgdx *dx;
323 	struct apc_mem *apc = apx->apx_hmem;
324 	struct mbuf *m;
325 	int len;
326 
327 	if ((ifp->if_flags & IFF_RUNNING) == 0)
328 		return (0);
329 	do {
330 		dx = apc->apc_txmd + apx->apx_txnum;
331 		if (dx->sgdx_flags & SG_OWN)
332 			return (0);
333 		IF_DEQUEUE(&ifp->if_snd, m);
334 		if (m == 0)
335 			return (0);
336 		len = min(m->m_pkthdr.len, SGMTU);
337 		m_copydata(m, 0, len, apc->apc_tbuf[apx->apx_txnum]);
338 		m_freem(m);
339 		dx->sgdx_mcnt = -len;
340 		dx->sgdx_flags = (SG_OWN|SG_TUI|SG_SLF|SG_ELF) |
341 			(0xff & dx->sgdx_flags);
342 		SG_WCSR(apx, 0, SG_INEA | SG_TDMD);
343 		DELAY(20);
344 		if (++apx->apx_txnum >= SGTBUF)
345 			apx->apx_txnum = 0;
346 	} while (++apx->apx_txcnt < SGTBUF);
347 	apx->apx_txcnt = SGTBUF; /* in case txcnt > SGTBUF by mistake */
348 	ifp->if_flags |= IFF_OACTIVE;
349 	return (0);
350 }
351 
352 void
353 apxintr()
354 {
355 	register struct apx_softc *apx;
356 	int reply;
357 
358 	apxstat.anyint++;
359 	for (apx = apx_softc + NAPX + NAPX; --apx >= apx_softc;) {
360 		if (apx->apx_flags & APXF_CHIPHERE)
361 		    /* Try to turn off interrupt cause */
362 		    while ((reply = SG_RCSR(apx, 0)) & 0xff) {
363 			SG_WCSR(apx, 0, SG_INEA | 0xfe);
364 			if (reply & (SG_MERR|SG_TUR|SG_ROR)) {
365 				apxerror(apx, "mem, rx, or tx error", reply);
366 				apxinit(apx->apx_if.if_unit);
367 				break;
368 			}
369 			if (reply & SG_RINT)
370 				apxrint(apx);
371 			if (reply & SG_TINT)
372 				apxtint(apx);
373 			if (reply & SG_PINT)
374 				apxstat.pint++;
375 		}
376 	}
377 }
378 
379 void
380 apxtint(apx)
381 	register struct apx_softc *apx;
382 {
383 	register struct apc_mem *apc = apx->apx_hmem;
384 	int i, loopcount = 0;
385 
386 	apxstat.tint++;
387 	do {
388 		if ((i = apx->apx_txnum - apx->apx_txcnt) < 0)
389 			i += SGTBUF;
390 		if (apc->apc_txmd[i].sgdx_flags & SG_OWN) {
391 			if (loopcount)
392 				break;
393 			apxstat.txnull++;
394 			return;
395 		}
396 		loopcount++;
397 		apx->apx_if.if_flags &= ~IFF_OACTIVE;
398 	} while (--apx->apx_txcnt > 0);
399 	apxstart(&apx->apx_if);
400 }
401 
402 void
403 apxrint(apx)
404 	register struct apx_softc *apx;
405 {
406 	register struct apc_mem *apc = apx->apx_hmem;
407 	register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum;
408 	int i = 0;
409 #define SGNEXTRXMD \
410 dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
411 
412 	apxstat.rint++;
413 	/*
414 	 * Out of sync with hardware, should never happen?
415 	 */
416 	while (dx->sgdx_flags & SG_OWN) {
417 		apxstat.rxnrdy++;
418 		if (++i == SGRBUF) {
419 			apxstat.rxnull++;
420 			return;
421 		}
422 		SGNEXTRXMD;
423 	}
424 	/*
425 	 * Process all buffers with valid data
426 	 */
427 	while ((dx->sgdx_flags & SG_OWN) == 0) {
428 		if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) {
429 			/*
430 			 * Find the end of the packet so we synch up.
431 			 * We throw the data away.
432 			 */
433 			apxerror(apx, "chained buffer", dx->sgdx_flags);
434 			do {
435 				apxstat.rx2big++;
436 				dx->sgdx_bcnt = 0;
437 				dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags);
438 				SGNEXTRXMD;
439 			} while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)));
440 			/*
441 			 * If search terminated without successful completion
442 			 * we reset the hardware (conservative).
443 			 */
444 			if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) !=
445 			    SG_ELF) {
446 				apxreset(apx->apx_if.if_unit);
447 				return;
448 			}
449 		} else
450 			apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum],
451 					-dx->sgdx_mcnt);
452 		dx->sgdx_bcnt = 0;
453 		dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags);
454 		SGNEXTRXMD;
455 	}
456 }
457 
458 void
459 apxinput(ifp, buffer, len)
460 	register struct ifnet *ifp;
461 	caddr_t buffer;
462 {
463 	extern struct ifqueue hdintrq, ipintrq;
464 	register struct ifqueue *inq;
465 	register u_char *cp = (u_char *)buffer;
466 	struct mbuf *m, *m_devget();
467 	int isr;
468 
469 	ifp->if_ipackets++;
470 	if ((ifp->if_flags & IFF_UP) == 0) {
471 		apxstat.nxpctd++;
472 		return;
473 	}
474 	if (cp[0] == 0xff && cp[1] == 0x3) {
475 		/* This is a UI HDLC Packet, so we'll assume PPP
476 		   protocol.  for now, IP only. */
477 		buffer += 4;
478 		len -= 4;
479 		inq = &ipintrq;
480 		isr = NETISR_IP;
481 	} else {
482 #ifdef CCITT
483 		inq = &hdintrq;
484 		isr = NETISR_CCITT;
485 	}
486 	if (len <= 0) {
487 #endif
488 		return;
489 	}
490 	m = m_devget(buffer, len, 0, ifp, (void (*)())0);
491 	if (m == 0)
492 		return;
493 	if(IF_QFULL(inq)) {
494 		IF_DROP(inq);
495 		m_freem(m);
496 	} else {
497 		apxstat.queued++;
498 		IF_ENQUEUE(inq, m);
499 		schednetisr(isr);
500 	}
501 }
502 
503 /*
504  * Process an ioctl request.
505  */
506 apxioctl(ifp, cmd, data)
507 	register struct ifnet *ifp;
508 	int cmd;
509 	caddr_t data;
510 {
511 	register struct ifaddr *ifa = (struct ifaddr *)data;
512 	int s = splimp(), error = 0;
513 	struct apx_softc *apx = &apx_softc[ifp->if_unit];
514 
515 	switch (cmd) {
516 
517 	case SIOCSIFADDR:
518 #ifdef CCITT
519 		ifa->ifa_rtrequest = x25_rtrequest;
520 		break;
521 
522 	case SIOCSIFCONF_X25:
523 		ifp->if_output = x25_ifoutput;
524 		ifp->if_flags |= IFF_UP;
525 		error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
526 		if (error == 0)
527 			apxinit(ifp->if_unit);
528 #endif
529 		break;
530 
531 	case SIOCSIFFLAGS:
532 		if (((ifp->if_flags & IFF_UP) == 0 &&
533 		     (ifp->if_flags & IFF_RUNNING)) ||
534 		    (ifp->if_flags & IFF_UP) &&
535 		     (ifp->if_flags & IFF_RUNNING) == 0)
536 			apxinit(ifp->if_unit);
537 		break;
538 
539 	case SIOCSIFMODE:
540 		if ((ifp->if_flags & IFF_UP) == 0)
541 			apx->apx_modes = *(struct apc_modes *)data;
542 		else
543 	default:
544 			error = EINVAL;
545 
546 	}
547 	splx(s);
548 	return (error);
549 }
550 
551 apxerror(apx, msg, data)
552 	register struct	apx_softc *apx;
553 	char	*msg;
554 {
555 	log(LOG_WARNING, "apc%d: %s, stat=0x%x\n",
556 		apx->apx_if.if_unit, msg, data);
557 }
558 
559 /*
560  * For debugging loopback activity.
561  */
562 apxoutput(ifp, m, dst, rt)
563 register struct ifnet *ifp;
564 register struct mbuf *m;
565 struct sockaddr *dst;
566 struct rtentry *rt;
567 {
568 	int s = splimp(), error = 0;
569 	static char pppheader[4] = { -1, 3, 0, 0x21 };
570 	/*
571 	 * Queue message on interface, and start output if interface
572 	 * not yet active.
573 	 */
574 	ifp->if_opackets++;
575 	M_PREPEND(m, sizeof pppheader, M_DONTWAIT);
576 	if (m == 0) {
577 		splx(s);
578 		return ENOBUFS;
579 	}
580 	bcopy(pppheader, mtod(m, caddr_t), sizeof pppheader);
581 	if (IF_QFULL(&ifp->if_snd)) {
582 		IF_DROP(&ifp->if_snd);
583 		m_freem(m);
584 		error = ENOBUFS;
585 	} else {
586 		IF_ENQUEUE(&ifp->if_snd, m);
587 		if ((ifp->if_flags & IFF_OACTIVE) == 0)
588 			(*ifp->if_start)(ifp);
589 	}
590 	splx(s);
591 	return (error);
592 }
593 #endif /* NAPX */
594