xref: /original-bsd/sys/i386/isa/if_apx.c (revision 753853ba)
1 /*
2  * Copyright (c) 1982, 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)if_apx.c	7.8 (Berkeley) 03/05/92
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 "param.h"
24 #include "mbuf.h"
25 #include "socket.h"
26 #include "ioctl.h"
27 #include "errno.h"
28 #include "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 "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 "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, subunit, unit = id->id_unit << 1;
117 	struct	apc_reg *reg = (struct apc_reg *)id->id_iobase;
118 	register struct	apx_softc *apx = apx_softc + unit;
119 
120 	for (subunit = 0; subunit < 2; subunit++) {
121 		apx->apx_msize	= id->id_msize >> 1;
122 		apx->apx_hmem	= (struct apc_mem *) (id->id_maddr + moffset);
123 		apx->apx_dmem	= (struct apc_mem *) moffset;
124 		apx->apx_device	= (caddr_t) id;
125 		apx->apx_reg	= reg;
126 		apx->apx_sgcp	= reg->axr_sgcp + subunit;
127 		apx->apx_csr4	= 0x0210;	/* no byte swapping for PC-AT */
128 		apx->apx_modes	= apx_default_modes;
129 		apx->apx_if.if_unit = unit++;
130 		apxtest(apx++);
131 		moffset = apx->apx_msize;
132 	}
133 	return 1;
134 }
135 
136 apxattach(id)
137 	struct	isa_device *id;
138 {
139 	register struct	apx_softc *apx = apx_softc + (id->id_unit << 1);
140 
141 	apx_ifattach(&((apx++)->apx_if));
142 	apx_ifattach(&(apx->apx_if));
143 	return 0;
144 }
145 /* End bus & endian dependence */
146 
147 /*
148  * Interface exists: make available by filling in network interface
149  * record.  System will initialize the interface when it is ready
150  * to accept packets.
151  */
152 void
153 apx_ifattach(ifp)
154 	register struct ifnet *ifp;
155 {
156 	/*
157 	 * Initialize ifnet structure
158 	 */
159 	ifp->if_name	= "apc";
160 	ifp->if_mtu	= SGMTU;
161 	ifp->if_init	= apxinit;
162 	ifp->if_output	= apxoutput;
163 	ifp->if_start	= apxstart;
164 	ifp->if_ioctl	= apxioctl;
165 	ifp->if_reset	= apxreset;
166 	ifp->if_type	= apx_default_modes.apm_iftype;
167 	ifp->if_hdrlen	= 5;
168 	ifp->if_addrlen	= 8;
169 	if_attach(ifp);
170 }
171 /*
172  * Initialization of interface
173  */
174 apxinit(unit)
175 	int unit;
176 {
177 	struct ifnet *ifp = &apx_softc[unit].apx_if;
178 	int s = splimp();
179 
180 	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
181 	if (apxreset(unit) && (ifp->if_flags & IFF_UP)) {
182 		ifp->if_flags |= IFF_RUNNING;
183 		(void)apxstart(ifp);
184 	}
185 	splx(s);
186 	return 0;
187 }
188 
189 apxctr(apx)
190 	register struct apx_softc *apx;
191 {
192 	APX_WCSR(apx, axr_ccr, 0xB0); /* select ctr 2, write lsb+msb, mode 0 */
193 	APX_WCSR(apx, axr_cnt2, 0x1);
194 	APX_WCSR(apx, axr_cnt2, 0x0);
195 	DELAY(50);
196 	APX_WCSR(apx, axr_ccr, 0xE8); /* latch status, ctr 2; */
197 	return (APX_RCSR(apx, axr_cnt2));
198 }
199 
200 void
201 apxtest(apx)
202 	register struct apx_softc *apx;
203 {
204 	int i =  0;
205 
206 	if ((apx->apx_if.if_unit & 1) == 0 && (i = apxctr(apx)) == 0)
207 		apxerror(apx, "no response from timer chip", 0);
208 	if (SG_RCSR(apx, 1) & 0x8000)
209 		SG_WCSR(apx, 1, 0x8040);
210 	SG_WCSR(apx, 4, apx->apx_csr4);
211 	SG_WCSR(apx, 5, 0x08);		/* Set DTR mode in SGS thompson chip */
212 	if (((i = SG_RCSR(apx, 5)) & 0xff08) != 0x08)
213 		apxerror(apx, "no mk5025, csr5 high bits are", i);
214 	else
215 		apx->apx_flags |= APXF_CHIPHERE;
216 	(void) apx_uprim(apx, SG_STOP, "stop after probing");
217 }
218 
219 apxreset(unit)
220 	int	unit;
221 {
222 	register struct apx_softc *apx = &apx_softc[unit ^ 1];
223 	u_char apm_apxmode = 0, apm_apxaltmode = 0;
224 #define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
225 
226 	MODE(apm_apxmode);
227 	MODE(apm_apxaltmode);
228 	apx = apx_softc + unit;
229 	MODE(apm_apxmode);
230 	MODE(apm_apxaltmode);
231 	APX_WCSR(apx, axr_mode, apm_apxmode);
232 	APX_WCSR(apx, axr_altmode, apm_apxaltmode);
233 	(void) apxctr(apx);
234 	(void) apx_uprim(apx, SG_STOP, "stop to reset");
235 	if ((apx->apx_if.if_flags & IFF_UP) == 0)
236 		return 0;
237 	apx_meminit(apx->apx_hmem, apx);
238 	SG_WCSR(apx, 4, apx->apx_csr4);
239 	SG_WCSR(apx, 2, apx->apx_csr23.f_hi);
240 	SG_WCSR(apx, 3, apx->apx_csr23.lo);
241 	if (apx_uprim(apx, SG_INIT, "init request") ||
242 	    apx_uprim(apx, SG_STAT, "status request") ||
243 	    apx_uprim(apx, SG_TRANS, "transparent mode"))
244 		return 0;
245 	SG_WCSR(apx, 0, SG_INEA);
246 	return 1;
247 }
248 
249 apx_uprim(apx, request, ident)
250 	register struct apx_softc *apx;
251 	char *ident;
252 {
253 	register int timo = 0;
254 	int reply;
255 
256 	if ((apx->apx_flags & APXF_CHIPHERE) == 0)
257 		return 1;	/* maybe even should panic . . . */
258 	if ((reply = SG_RCSR(apx, 1)) & 0x8040)
259 		SG_WCSR(apx, 1, 0x8040); /* Magic! */
260 	if (request == SG_STOP && (SG_RCSR(apx, 0) & SG_STOPPED))
261 		return 0;
262 	SG_WCSR(apx, 1, request | SG_UAV);
263 	do {
264 		reply = SG_RCSR(apx, 1);
265 		if (timo++ >= TIMO || (reply & 0x8000)) {
266 				apxerror(apx, ident, reply);
267 				return 1;
268 		}
269 	} while (reply & SG_UAV);
270 	return 0;
271 }
272 
273 apx_meminit(apc, apx)
274 	register struct apc_mem *apc;
275 	struct apx_softc *apx;
276 {
277 	register struct apc_mem *apcbase = apx->apx_dmem;
278 	register int i;
279 #define LOWADDR(e) (((u_long)&(apcbase->e)) & 0xffff)
280 #define HIADDR(e) ((((u_long)&(apcbase->e)) >> 16) & 0xff)
281 #define SET_SGAE(d, f, a) {(d).lo = LOWADDR(a); (d).f_hi = (f) | HIADDR(a);}
282 #define SET_SGDX(d, f, a, b) \
283 	{SET_SGAE((d).sgdx_ae, f, a); (d).sgdx_mcnt = (d).sgdx_bcnt = (b);}
284 
285 	apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = 0;
286 	bzero((caddr_t)apc, ((caddr_t)(&apc->apc_rxmd[0])) - (caddr_t)apc);
287 	apc->apc_mode = 0x0108;	/* 2 flag spacing, leave addr & ctl, do CRC16 */
288 	apc->apc_sgop = apx->apx_modes.apm_sgop;
289 	SET_SGAE(apx->apx_csr23, SG_UIE | SG_PROM, apc_mode);
290 	SET_SGAE(apc->apc_rxdd, SG_RLEN, apc_rxmd[0]);
291 	i = SG_TLEN | ((apx->apx_modes.apm_txwin)<< 8);
292 	SET_SGAE(apc->apc_txdd, i, apc_txmd[0]);
293 	SET_SGAE(apc->apc_stdd, 0, apc_sgsb);
294 	SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf[0], -SGMTU);
295 	SET_SGDX(apc->apc_txtid, 0, apc_txidbuf[0], 0);
296 	for (i = 0; i < SGRBUF; i++)
297 		 SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU)
298 	for (i = 0; i < SGTBUF; i++)
299 		 SET_SGDX(apc->apc_txmd[i], 0, apc_tbuf[i][0], 0)
300 }
301 
302 /*
303  * Start output on interface.  Get another datagram to send
304  * off of the interface queue, and copy it to the interface
305  * before starting the output.
306  */
307 apxstart(ifp)
308 	struct ifnet *ifp;
309 {
310 	register struct apx_softc *apx = &apx_softc[ifp->if_unit];
311 	register struct sgdx *dx;
312 	struct apc_mem *apc = apx->apx_hmem;
313 	struct mbuf *m;
314 	int len;
315 
316 	if ((ifp->if_flags & IFF_RUNNING) == 0)
317 		return (0);
318 	do {
319 		dx = apc->apc_txmd + apx->apx_txnum;
320 		if (dx->sgdx_flags & SG_OWN)
321 			return (0);
322 		IF_DEQUEUE(&ifp->if_snd, m);
323 		if (m == 0)
324 			return (0);
325 		len = min(m->m_pkthdr.len, SGMTU);
326 		m_copydata(m, 0, len, apc->apc_tbuf[apx->apx_txnum]);
327 		dx->sgdx_mcnt = -len;
328 		dx->sgdx_flags = (SG_OWN|SG_TUI|SG_SLF|SG_ELF) |
329 			(0xff & dx->sgdx_flags);
330 		SG_WCSR(apx, 0, SG_INEA | SG_TDMD);
331 		DELAY(20);
332 		if (++apx->apx_txnum >= SGTBUF)
333 			apx->apx_txnum = 0;
334 	} while (++apx->apx_txcnt < SGTBUF);
335 	apx->apx_txcnt = SGTBUF; /* in case txcnt > SGTBUF by mistake */
336 	ifp->if_flags |= IFF_OACTIVE;
337 	return (0);
338 }
339 
340 void
341 apxintr()
342 {
343 	register struct apx_softc *apx;
344 	int reply;
345 
346 	apxstat.anyint++;
347 	for (apx = apx_softc + NAPX + NAPX; --apx >= apx_softc;) {
348 		if (apx->apx_flags & APXF_CHIPHERE)
349 		    /* Try to turn off interrupt cause */
350 		    while ((reply = SG_RCSR(apx, 0)) & 0xff) {
351 			SG_WCSR(apx, 0, SG_INEA | 0xfe);
352 			if (reply & (SG_MERR|SG_TUR|SG_ROR)) {
353 				apxerror(apx, "mem, rx, or tx error", reply);
354 				apxinit(apx->apx_if.if_unit);
355 				break;
356 			}
357 			if (reply & SG_RINT)
358 				apxrint(apx);
359 			if (reply & SG_TINT)
360 				apxtint(apx);
361 			if (reply & SG_PINT)
362 				apxstat.pint++;
363 		}
364 	}
365 }
366 
367 void
368 apxtint(apx)
369 	register struct apx_softc *apx;
370 {
371 	register struct apc_mem *apc = apx->apx_hmem;
372 	int i, loopcount = 0;
373 
374 	apxstat.tint++;
375 	do {
376 		if ((i = apx->apx_txnum - apx->apx_txcnt) < 0)
377 			i += SGTBUF;
378 		if (apc->apc_txmd[i].sgdx_flags & SG_OWN) {
379 			if (loopcount)
380 				break;
381 			apxstat.txnull++;
382 			return;
383 		}
384 		loopcount++;
385 		apx->apx_if.if_flags &= ~IFF_OACTIVE;
386 	} while (--apx->apx_txcnt > 0);
387 	apxstart(&apx->apx_if);
388 }
389 
390 void
391 apxrint(apx)
392 	register struct apx_softc *apx;
393 {
394 	register struct apc_mem *apc = apx->apx_hmem;
395 	register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum;
396 	int i = 0;
397 #define SGNEXTRXMD \
398 dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
399 
400 	apxstat.rint++;
401 	/*
402 	 * Out of sync with hardware, should never happen?
403 	 */
404 	while (dx->sgdx_flags & SG_OWN) {
405 		apxstat.rxnrdy++;
406 		if (++i == SGRBUF) {
407 			apxstat.rxnull++;
408 			return;
409 		}
410 		SGNEXTRXMD;
411 	}
412 	/*
413 	 * Process all buffers with valid data
414 	 */
415 	while ((dx->sgdx_flags & SG_OWN) == 0) {
416 		if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) {
417 			/*
418 			 * Find the end of the packet so we synch up.
419 			 * We throw the data away.
420 			 */
421 			apxerror(apx, "chained buffer", dx->sgdx_flags);
422 			do {
423 				apxstat.rx2big++;
424 				dx->sgdx_bcnt = 0;
425 				dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags);
426 				SGNEXTRXMD;
427 			} while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)));
428 			/*
429 			 * If search terminated without successful completion
430 			 * we reset the hardware (conservative).
431 			 */
432 			if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) !=
433 			    SG_ELF) {
434 				apxreset(apx->apx_if.if_unit);
435 				return;
436 			}
437 		} else
438 			apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum],
439 					-dx->sgdx_mcnt);
440 		dx->sgdx_bcnt = 0;
441 		dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags);
442 		SGNEXTRXMD;
443 	}
444 }
445 
446 void
447 apxinput(ifp, buffer, len)
448 	register struct ifnet *ifp;
449 	caddr_t buffer;
450 {
451 	extern struct ifqueue hdintrq, ipintrq;
452 	register struct ifqueue *inq;
453 	register u_char *cp = (u_char *)buffer;
454 	struct mbuf *m, *m_devget();
455 	int isr;
456 
457 	ifp->if_ipackets++;
458 	if ((ifp->if_flags & IFF_UP) == 0) {
459 		apxstat.nxpctd++;
460 		return;
461 	}
462 	if (cp[0] == 0xff && cp[1] == 0x3) {
463 		/* This is a UI HDLC Packet, so we'll assume PPP
464 		   protocol.  for now, IP only. */
465 		buffer += 4;
466 		len -= 4;
467 		inq = &ipintrq;
468 		isr = NETISR_IP;
469 	} else {
470 #ifdef CCITT
471 		inq = &hdintrq;
472 		isr = NETISR_CCITT;
473 	}
474 	if (len <= 0) {
475 #endif
476 		return;
477 	}
478 	m = m_devget(buffer, len, 0, ifp, (void (*)())0);
479 	if (m == 0)
480 		return;
481 	if(IF_QFULL(inq)) {
482 		IF_DROP(inq);
483 		m_freem(m);
484 	} else {
485 		apxstat.queued++;
486 		IF_ENQUEUE(inq, m);
487 		schednetisr(isr);
488 	}
489 }
490 
491 /*
492  * Process an ioctl request.
493  */
494 apxioctl(ifp, cmd, data)
495 	register struct ifnet *ifp;
496 	int cmd;
497 	caddr_t data;
498 {
499 	register struct ifaddr *ifa = (struct ifaddr *)data;
500 	int s = splimp(), error = 0;
501 	struct apx_softc *apx = &apx_softc[ifp->if_unit];
502 
503 	switch (cmd) {
504 
505 	case SIOCSIFADDR:
506 #ifdef CCITT
507 		ifa->ifa_rtrequest = x25_rtrequest;
508 		break;
509 
510 	case SIOCSIFCONF_X25:
511 		ifp->if_output = x25_ifoutput;
512 		ifp->if_flags |= IFF_UP;
513 		error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
514 		if (error == 0)
515 			apxinit(ifp->if_unit);
516 #endif
517 		break;
518 
519 	case SIOCSIFFLAGS:
520 		if (((ifp->if_flags & IFF_UP) == 0 &&
521 		     (ifp->if_flags & IFF_RUNNING)) ||
522 		    (ifp->if_flags & IFF_UP) &&
523 		     (ifp->if_flags & IFF_RUNNING) == 0)
524 			apxinit(ifp->if_unit);
525 		break;
526 
527 	case SIOCSIFMODE:
528 		if ((ifp->if_flags & IFF_UP) == 0)
529 			apx->apx_modes = *(struct apc_modes *)data;
530 		else
531 	default:
532 			error = EINVAL;
533 
534 	}
535 	splx(s);
536 	return (error);
537 }
538 
539 apxerror(apx, msg, data)
540 	register struct	apx_softc *apx;
541 	char	*msg;
542 {
543 	log(LOG_WARNING, "apc%d: %s, stat=0x%x\n",
544 		apx->apx_if.if_unit, msg, data);
545 }
546 
547 /*
548  * For debugging loopback activity.
549  */
550 apxoutput(ifp, m, dst, rt)
551 register struct ifnet *ifp;
552 register struct mbuf *m;
553 struct sockaddr *dst;
554 struct rtentry *rt;
555 {
556 	int s = splimp(), error = 0;
557 	static char pppheader[4] = { -1, 3, 0, 0x21 };
558 	/*
559 	 * Queue message on interface, and start output if interface
560 	 * not yet active.
561 	 */
562 	ifp->if_opackets++;
563 	M_PREPEND(m, sizeof pppheader, M_DONTWAIT);
564 	if (m == 0) {
565 		splx(s);
566 		return ENOBUFS;
567 	}
568 	bcopy(pppheader, mtod(m, caddr_t), sizeof pppheader);
569 	if (IF_QFULL(&ifp->if_snd)) {
570 		IF_DROP(&ifp->if_snd);
571 		m_freem(m);
572 		error = ENOBUFS;
573 	} else {
574 		IF_ENQUEUE(&ifp->if_snd, m);
575 		if ((ifp->if_flags & IFF_OACTIVE) == 0)
576 			(*ifp->if_start)(ifp);
577 	}
578 	splx(s);
579 	return (error);
580 }
581 #endif /* NAPX */
582