123281Smckusick /*
234868Sbostic * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
333462Skarels * All rights reserved.
423281Smckusick *
544555Sbostic * %sccs.include.redist.c%
633462Skarels *
7*45801Sbostic * @(#)if_acc.c 7.8 (Berkeley) 12/16/90
823281Smckusick */
95638Ssam
105638Ssam #include "acc.h"
1123700Skarels #if NACC > 0
125638Ssam
135638Ssam /*
145638Ssam * ACC LH/DH ARPAnet IMP interface driver.
155638Ssam */
1637508Smckusick #include "machine/pte.h"
175638Ssam
18*45801Sbostic #include "sys/param.h"
19*45801Sbostic #include "sys/systm.h"
20*45801Sbostic #include "sys/mbuf.h"
21*45801Sbostic #include "sys/buf.h"
22*45801Sbostic #include "sys/protosw.h"
23*45801Sbostic #include "sys/socket.h"
24*45801Sbostic #include "sys/vmmac.h"
258458Sroot
26*45801Sbostic #include "net/if.h"
27*45801Sbostic #include "netimp/if_imp.h"
288458Sroot
29*45801Sbostic #include "../include/cpu.h"
30*45801Sbostic #include "../include/mtpr.h"
3117109Sbloom #include "if_accreg.h"
3217109Sbloom #include "if_uba.h"
33*45801Sbostic #include "../uba/ubareg.h"
34*45801Sbostic #include "../uba/ubavar.h"
355638Ssam
365638Ssam int accprobe(), accattach(), accrint(), accxint();
375638Ssam struct uba_device *accinfo[NACC];
385638Ssam u_short accstd[] = { 0 };
395638Ssam struct uba_driver accdriver =
405638Ssam { accprobe, 0, accattach, 0, accstd, "acc", accinfo };
415638Ssam
4233452Skarels int accinit(), accoutput(), accdown(), accreset();
435638Ssam
445638Ssam /*
455638Ssam * "Lower half" of IMP interface driver.
465638Ssam *
475638Ssam * Each IMP interface is handled by a common module which handles
485638Ssam * the IMP-host protocol and a hardware driver which manages the
495638Ssam * hardware specific details of talking with the IMP.
505638Ssam *
515638Ssam * The hardware portion of the IMP driver handles DMA and related
525638Ssam * management of UNIBUS resources. The IMP protocol module interprets
535638Ssam * contents of these messages and "controls" the actions of the
545638Ssam * hardware module during IMP resets, but not, for instance, during
555638Ssam * UNIBUS resets.
565638Ssam *
575638Ssam * The two modules are coupled at "attach time", and ever after,
585638Ssam * through the imp interface structure. Higher level protocols,
595638Ssam * e.g. IP, interact with the IMP driver, rather than the ACC.
605638Ssam */
615638Ssam struct acc_softc {
6233452Skarels struct imp_softc *acc_imp; /* data structure shared with IMP */
635638Ssam struct ifuba acc_ifuba; /* UNIBUS resources */
645638Ssam struct mbuf *acc_iq; /* input reassembly queue */
655638Ssam short acc_olen; /* size of last message sent */
665638Ssam char acc_flush; /* flush remainder of message */
675638Ssam } acc_softc[NACC];
685638Ssam
695638Ssam /*
705638Ssam * Reset the IMP and cause a transmitter interrupt by
715638Ssam * performing a null DMA.
725638Ssam */
accprobe(reg)735638Ssam accprobe(reg)
745638Ssam caddr_t reg;
755638Ssam {
765638Ssam register int br, cvec; /* r11, r10 value-result */
775638Ssam register struct accdevice *addr = (struct accdevice *)reg;
785638Ssam
795638Ssam #ifdef lint
805638Ssam br = 0; cvec = br; br = cvec;
815638Ssam accrint(0); accxint(0);
825638Ssam #endif
835771Swnj addr->icsr = ACC_RESET; DELAY(5000);
845771Swnj addr->ocsr = ACC_RESET; DELAY(5000);
855771Swnj addr->ocsr = OUT_BBACK; DELAY(5000);
865771Swnj addr->owc = 0;
875771Swnj addr->ocsr = ACC_IE | ACC_GO; DELAY(5000);
887176Sroot addr->ocsr = 0;
895771Swnj if (cvec && cvec != 0x200) /* transmit -> receive */
905638Ssam cvec -= 4;
915638Ssam return (1);
925638Ssam }
935638Ssam
945638Ssam /*
955638Ssam * Call the IMP module to allow it to set up its internal
965638Ssam * state, then tie the two modules together by setting up
975638Ssam * the back pointers to common data structures.
985638Ssam */
accattach(ui)995638Ssam accattach(ui)
10034273Skarels register struct uba_device *ui;
1015638Ssam {
1025638Ssam register struct acc_softc *sc = &acc_softc[ui->ui_unit];
1035638Ssam register struct impcb *ip;
1045638Ssam
10534273Skarels if ((sc->acc_imp = impattach(ui->ui_driver->ud_dname, ui->ui_unit,
10634273Skarels accreset)) == 0)
10724799Skarels return;
10833452Skarels ip = &sc->acc_imp->imp_cb;
1095638Ssam ip->ic_init = accinit;
11033452Skarels ip->ic_output = accoutput;
11133452Skarels ip->ic_down = accdown;
1128837Sroot sc->acc_ifuba.ifu_flags = UBA_CANTWAIT;
1135771Swnj #ifdef notdef
1147169Ssam sc->acc_ifuba.ifu_flags |= UBA_NEEDBDP;
1155771Swnj #endif
1165638Ssam }
1175638Ssam
1185638Ssam /*
1195638Ssam * Reset interface after UNIBUS reset.
1205638Ssam * If interface is on specified uba, reset its state.
1215638Ssam */
accreset(unit,uban)1225638Ssam accreset(unit, uban)
1235638Ssam int unit, uban;
1245638Ssam {
1255638Ssam register struct uba_device *ui;
1265638Ssam struct acc_softc *sc;
1275638Ssam
1285638Ssam if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0 ||
1295638Ssam ui->ui_ubanum != uban)
1305638Ssam return;
1315638Ssam printf(" acc%d", unit);
1325638Ssam sc = &acc_softc[unit];
13333452Skarels sc->acc_imp->imp_if.if_flags &= ~IFF_RUNNING;
13434273Skarels accoflush(unit);
1355638Ssam /* must go through IMP to allow it to set state */
13633452Skarels (*sc->acc_imp->imp_if.if_init)(sc->acc_imp->imp_if.if_unit);
1375638Ssam }
1385638Ssam
1395638Ssam /*
1405638Ssam * Initialize interface: clear recorded pending operations,
1415771Swnj * and retrieve, and initialize UNIBUS resources. Note
1425771Swnj * return value is used by IMP init routine to mark IMP
1435771Swnj * unavailable for outgoing traffic.
1445638Ssam */
accinit(unit)1455638Ssam accinit(unit)
1465638Ssam int unit;
1475638Ssam {
1485771Swnj register struct acc_softc *sc;
1495771Swnj register struct uba_device *ui;
1505638Ssam register struct accdevice *addr;
1518615Sroot int info;
1525638Ssam
1535771Swnj if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0) {
1545771Swnj printf("acc%d: not alive\n", unit);
1555771Swnj return (0);
1565771Swnj }
1575771Swnj sc = &acc_softc[unit];
1585771Swnj /*
1595771Swnj * Header length is 0 since we have to passs
1605771Swnj * the IMP leader up to the protocol interpretation
1615771Swnj * routines. If we had the header length as
1625771Swnj * sizeof(struct imp_leader), then the if_ routines
1635771Swnj * would asssume we handle it on input and output.
1645771Swnj */
16533452Skarels if ((sc->acc_imp->imp_if.if_flags & IFF_RUNNING) == 0 &&
16633452Skarels if_ubainit(&sc->acc_ifuba, ui->ui_ubanum, 0,
16733452Skarels (int)btoc(IMP_RCVBUF)) == 0) {
1685638Ssam printf("acc%d: can't initialize\n", unit);
16933452Skarels sc->acc_imp->imp_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
1707169Ssam return (0);
1715638Ssam }
17233452Skarels sc->acc_imp->imp_if.if_flags |= IFF_RUNNING;
1735638Ssam addr = (struct accdevice *)ui->ui_addr;
1745638Ssam
1755647Ssam /*
1765771Swnj * Reset the imp interface;
1775771Swnj * the delays are pure guesswork.
1785647Ssam */
1795771Swnj addr->ocsr = ACC_RESET; DELAY(5000);
1806258Sroot addr->ocsr = OUT_BBACK; DELAY(5000); /* reset host master ready */
1815771Swnj addr->ocsr = 0;
1827169Ssam if (accinputreset(addr, unit) == 0) {
1837169Ssam ui->ui_alive = 0;
1847169Ssam return (0);
1855638Ssam }
1865638Ssam
1875638Ssam /*
1885638Ssam * Put up a read. We can't restart any outstanding writes
1895638Ssam * until we're back in synch with the IMP (i.e. we've flushed
1905638Ssam * the NOOPs it throws at us).
19133452Skarels * Note: IMP_RCVBUF includes the leader.
1925638Ssam */
1935638Ssam info = sc->acc_ifuba.ifu_r.ifrw_info;
1945771Swnj addr->iba = (u_short)info;
19533452Skarels addr->iwc = -((IMP_RCVBUF) >> 1);
1965771Swnj #ifdef LOOPBACK
1975771Swnj addr->ocsr |= OUT_BBACK;
1985771Swnj #endif
1995771Swnj addr->icsr =
2005638Ssam IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO;
2015771Swnj return (1);
2025638Ssam }
2035638Ssam
accinputreset(addr,unit)2047169Ssam accinputreset(addr, unit)
2057169Ssam register struct accdevice *addr;
2067169Ssam register int unit;
2077169Ssam {
2087169Ssam register int i;
2097169Ssam
2107169Ssam addr->icsr = ACC_RESET; DELAY(5000);
2117169Ssam addr->icsr = IN_MRDY | IN_WEN; /* close the relay */
2127169Ssam DELAY(10000);
2137169Ssam /* YECH!!! */
2147169Ssam for (i = 0; i < 500; i++) {
2157169Ssam if ((addr->icsr & IN_HRDY) ||
2167169Ssam (addr->icsr & (IN_RMR | IN_IMPBSY)) == 0)
2177169Ssam return (1);
2187169Ssam addr->icsr = IN_MRDY | IN_WEN; DELAY(10000);
2197169Ssam /* keep turning IN_RMR off */
2207169Ssam }
2217169Ssam printf("acc%d: imp doesn't respond, icsr=%b\n", unit,
2227169Ssam addr->icsr, ACC_INBITS);
2237169Ssam return (0);
2247169Ssam }
2257169Ssam
2265638Ssam /*
22733452Skarels * Drop the host ready line to mark host down.
22833452Skarels */
accdown(unit)22933452Skarels accdown(unit)
23033452Skarels int unit;
23133452Skarels {
23233452Skarels register struct accdevice *addr;
23333452Skarels
23433452Skarels addr = (struct accdevice *)(accinfo[unit]->ui_addr);
23533452Skarels addr->ocsr = ACC_RESET;
23633452Skarels DELAY(5000);
23733452Skarels addr->ocsr = OUT_BBACK; /* reset host master ready */
23834273Skarels accoflush(unit);
23933452Skarels return (1);
24033452Skarels }
24133452Skarels
accoflush(unit)24234273Skarels accoflush(unit)
24334273Skarels int unit;
24434273Skarels {
24534273Skarels register struct acc_softc *sc = &acc_softc[unit];
24634273Skarels
24734273Skarels sc->acc_imp->imp_cb.ic_oactive = 0;
24834273Skarels if (sc->acc_ifuba.ifu_xtofree) {
24934273Skarels m_freem(sc->acc_ifuba.ifu_xtofree);
25034273Skarels sc->acc_ifuba.ifu_xtofree = 0;
25134273Skarels }
25234273Skarels }
25334273Skarels
25433452Skarels /*
2555638Ssam * Start output on an interface.
2565638Ssam */
accoutput(unit,m)25733452Skarels accoutput(unit, m)
25833452Skarels int unit;
25933452Skarels struct mbuf *m;
2605638Ssam {
26133452Skarels int info;
2625638Ssam register struct acc_softc *sc = &acc_softc[unit];
2635638Ssam register struct accdevice *addr;
2645638Ssam u_short cmd;
2655638Ssam
2665638Ssam sc->acc_olen = if_wubaput(&sc->acc_ifuba, m);
2675638Ssam /*
2685771Swnj * Have request mapped to UNIBUS for
2695771Swnj * transmission; start the output.
2705638Ssam */
2715771Swnj if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP)
2725771Swnj UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_w.ifrw_bdp);
2735923Ssam addr = (struct accdevice *)accinfo[unit]->ui_addr;
2745638Ssam info = sc->acc_ifuba.ifu_w.ifrw_info;
2755771Swnj addr->oba = (u_short)info;
2765771Swnj addr->owc = -((sc->acc_olen + 1) >> 1);
2775638Ssam cmd = ACC_IE | OUT_ENLB | ((info & 0x30000) >> 12) | ACC_GO;
2785771Swnj #ifdef LOOPBACK
2795771Swnj cmd |= OUT_BBACK;
2805771Swnj #endif
2815771Swnj addr->ocsr = cmd;
28233452Skarels sc->acc_imp->imp_cb.ic_oactive = 1;
2835638Ssam }
2845638Ssam
2855638Ssam /*
2865638Ssam * Output interrupt handler.
2875638Ssam */
accxint(unit)2885638Ssam accxint(unit)
2898414Swnj int unit;
2905638Ssam {
2915638Ssam register struct acc_softc *sc = &acc_softc[unit];
2925638Ssam register struct accdevice *addr;
2935638Ssam
2947169Ssam addr = (struct accdevice *)accinfo[unit]->ui_addr;
29533452Skarels if (sc->acc_imp->imp_cb.ic_oactive == 0) {
2967169Ssam printf("acc%d: stray xmit interrupt, csr=%b\n", unit,
2977169Ssam addr->ocsr, ACC_OUTBITS);
2985638Ssam return;
2995638Ssam }
30033452Skarels sc->acc_imp->imp_if.if_opackets++;
30133452Skarels sc->acc_imp->imp_cb.ic_oactive = 0;
3027176Sroot if (addr->ocsr & ACC_ERR) {
3037169Ssam printf("acc%d: output error, ocsr=%b, icsr=%b\n", unit,
3047169Ssam addr->ocsr, ACC_OUTBITS, addr->icsr, ACC_INBITS);
30533452Skarels sc->acc_imp->imp_if.if_oerrors++;
3065647Ssam }
3075771Swnj if (sc->acc_ifuba.ifu_xtofree) {
3085771Swnj m_freem(sc->acc_ifuba.ifu_xtofree);
3095771Swnj sc->acc_ifuba.ifu_xtofree = 0;
3105771Swnj }
31133452Skarels impstart(sc->acc_imp);
3125638Ssam }
3135638Ssam
3145638Ssam /*
3155638Ssam * Input interrupt handler
3165638Ssam */
accrint(unit)3175638Ssam accrint(unit)
3188414Swnj int unit;
3195638Ssam {
3205638Ssam register struct acc_softc *sc = &acc_softc[unit];
3215638Ssam register struct accdevice *addr;
3225638Ssam struct mbuf *m;
3235638Ssam int len, info;
3245638Ssam
3257169Ssam addr = (struct accdevice *)accinfo[unit]->ui_addr;
32633452Skarels sc->acc_imp->imp_if.if_ipackets++;
3275638Ssam
3285638Ssam /*
3295638Ssam * Purge BDP; flush message if error indicated.
3305638Ssam */
3315771Swnj if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP)
3325771Swnj UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_r.ifrw_bdp);
3337176Sroot if (addr->icsr & ACC_ERR) {
3347198Ssam printf("acc%d: input error, csr=%b\n", unit,
3357198Ssam addr->icsr, ACC_INBITS);
33633452Skarels sc->acc_imp->imp_if.if_ierrors++;
3375638Ssam sc->acc_flush = 1;
3385638Ssam }
3395638Ssam
3405638Ssam if (sc->acc_flush) {
3415771Swnj if (addr->icsr & IN_EOM)
3425638Ssam sc->acc_flush = 0;
3435638Ssam goto setup;
3445638Ssam }
34533452Skarels len = IMP_RCVBUF + (addr->iwc << 1);
34633452Skarels if (len < 0 || len > IMP_RCVBUF) {
34725441Skarels printf("acc%d: bad length=%d\n", unit, len);
34833452Skarels sc->acc_imp->imp_if.if_ierrors++;
3495771Swnj goto setup;
3505771Swnj }
3515638Ssam
3525638Ssam /*
35324799Skarels * The offset parameter is always 0 since using
3545638Ssam * trailers on the ARPAnet is insane.
3555638Ssam */
35633452Skarels m = if_rubaget(&sc->acc_ifuba, len, 0, &sc->acc_imp->imp_if);
3575638Ssam if (m == 0)
3585638Ssam goto setup;
3595771Swnj if ((addr->icsr & IN_EOM) == 0) {
3605647Ssam if (sc->acc_iq)
3615638Ssam m_cat(sc->acc_iq, m);
3625647Ssam else
3635638Ssam sc->acc_iq = m;
3645638Ssam goto setup;
3655638Ssam }
3665647Ssam if (sc->acc_iq) {
3675638Ssam m_cat(sc->acc_iq, m);
3685638Ssam m = sc->acc_iq;
3695638Ssam sc->acc_iq = 0;
3705638Ssam }
3715638Ssam impinput(unit, m);
3725638Ssam
3735638Ssam setup:
3745638Ssam /*
3755638Ssam * Setup for next message.
3765638Ssam */
3775638Ssam info = sc->acc_ifuba.ifu_r.ifrw_info;
3785771Swnj addr->iba = (u_short)info;
37933452Skarels addr->iwc = -((IMP_RCVBUF)>> 1);
3805771Swnj addr->icsr =
3815638Ssam IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO;
3825638Ssam }
3835638Ssam #endif
384