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
apxprobe(id)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
apx_ifattach(ifp)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 */
apxinit(unit)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
apxctr(apx)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
apxtest(apx)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
apxreset(unit)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
apx_uprim(apx,request,ident)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
apx_meminit(apc,apx)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
apxintr()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
apxtint(apx)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
apxrint(apx)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
apxinput(ifp,buffer,len)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 */
apxioctl(ifp,cmd,data)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
apxerror(apx,msg,data)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 */
apxoutput(ifp,m,dst,rt)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