diff options
-rw-r--r-- | sys/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/dev/ppbus/if_plip.c | 772 | ||||
-rw-r--r-- | sys/dev/ppbus/nlpt.c | 61 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_1284.c | 72 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_1284.h | 9 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_base.c | 63 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_msq.c | 326 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_msq.h | 190 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.c | 92 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.h | 168 | ||||
-rw-r--r-- | sys/dev/ppbus/pps.c | 3 | ||||
-rw-r--r-- | sys/dev/ppbus/vpo.c | 626 | ||||
-rw-r--r-- | sys/dev/ppbus/vpoio.c | 771 | ||||
-rw-r--r-- | sys/dev/ppbus/vpoio.h (renamed from sys/dev/ppbus/vpo.h) | 82 | ||||
-rw-r--r-- | sys/dev/ppc/ppc.c | 720 | ||||
-rw-r--r-- | sys/dev/ppc/ppcreg.h | 79 | ||||
-rw-r--r-- | sys/i386/conf/LINT | 4 | ||||
-rw-r--r-- | sys/i386/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/i386/isa/ppc.c | 720 | ||||
-rw-r--r-- | sys/i386/isa/ppcreg.h | 79 | ||||
-rw-r--r-- | sys/isa/ppc.c | 720 | ||||
-rw-r--r-- | sys/isa/ppcreg.h | 79 |
23 files changed, 4286 insertions, 1361 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 0ad0237..ab64a7a 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.446 1998/07/11 04:46:27 julian Exp $ +# $Id: LINT,v 1.447 1998/07/20 20:00:30 msmith Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1440,6 +1440,7 @@ options POWERFAIL_NMI # make it beep instead of panicing # Requires SCSI disk support ('scbus' and 'sd'), best # performance is achieved with ports in EPP 1.9 mode. # nlpt Parallel Printer +# plip Parallel network interface # ppi General-purpose I/O ("Geek Port") # # Supported interfaces: @@ -1448,6 +1449,7 @@ options POWERFAIL_NMI # make it beep instead of panicing controller ppbus0 controller vpo0 at ppbus? device nlpt0 at ppbus? +device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? diff --git a/sys/conf/files b/sys/conf/files index dd45be0..6d07bbf 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -53,10 +53,13 @@ dev/pdq/pdq_ifsubr.c optional fpa device-driver dev/ppbus/nlpt.c optional nlpt dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_1284.c optional ppbus +dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo +dev/ppbus/vpoio.c optional vpo +dev/ppbus/if_plip.c optional plip dev/slice/slice_base.c optional slice dev/slice/slice_device.c optional slice dev/slice/mbr.c optional slice diff --git a/sys/dev/ppbus/if_plip.c b/sys/dev/ppbus/if_plip.c new file mode 100644 index 0000000..b25abfc --- /dev/null +++ b/sys/dev/ppbus/if_plip.c @@ -0,0 +1,772 @@ +/*- + * Copyright (c) 1997 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp + * $Id$ + */ + +/* + * Parallel port TCP/IP interfaces added. I looked at the driver from + * MACH but this is a complete rewrite, and btw. incompatible, and it + * should perform better too. I have never run the MACH driver though. + * + * This driver sends two bytes (0x08, 0x00) in front of each packet, + * to allow us to distinguish another format later. + * + * Now added an Linux/Crynwr compatibility mode which is enabled using + * IF_LINK0 - Tim Wilkinson. + * + * TODO: + * Make HDLC/PPP mode, use IF_LLC1 to enable. + * + * Connect the two computers using a Laplink parallel cable to use this + * feature: + * + * +----------------------------------------+ + * |A-name A-End B-End Descr. Port/Bit | + * +----------------------------------------+ + * |DATA0 2 15 Data 0/0x01 | + * |-ERROR 15 2 1/0x08 | + * +----------------------------------------+ + * |DATA1 3 13 Data 0/0x02 | + * |+SLCT 13 3 1/0x10 | + * +----------------------------------------+ + * |DATA2 4 12 Data 0/0x04 | + * |+PE 12 4 1/0x20 | + * +----------------------------------------+ + * |DATA3 5 10 Strobe 0/0x08 | + * |-ACK 10 5 1/0x40 | + * +----------------------------------------+ + * |DATA4 6 11 Data 0/0x10 | + * |BUSY 11 6 1/~0x80 | + * +----------------------------------------+ + * |GND 18-25 18-25 GND - | + * +----------------------------------------+ + * + * Expect transfer-rates up to 75 kbyte/sec. + * + * If GCC could correctly grok + * register int port asm("edx") + * the code would be cleaner + * + * Poul-Henning Kamp <phk@freebsd.org> + */ + +/* + * Update for ppbus, PLIP support only - Nicolas Souchu + */ + +#ifdef KERNEL +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/filio.h> +#include <sys/sockio.h> +#include <sys/kernel.h> +#include <sys/time.h> +#include <sys/malloc.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> + +#endif /* KERNEL */ +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <net/netisr.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/nlpt.h> + +#ifndef LPMTU /* MTU for the lp# interfaces */ +#define LPMTU 1500 +#endif + +#ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */ +#define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */ +#endif + +#ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */ +#define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */ +#endif + +#ifndef LPMAXERRS /* Max errors before !RUNNING */ +#define LPMAXERRS 100 +#endif + +#define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ +#define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ +#define MLPIPHDRLEN CLPIPHDRLEN + +#define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */ +#define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */ +#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN +#define MLPIPHDRLEN LPIPHDRLEN +#endif + +#define LPIPTBLSIZE 256 /* Size of octet translation table */ + +#define DEBUG + +#ifndef DEBUG +#define lprintf (void) +#else +#define lprintf if (lptflag) printf +static int volatile lptflag = 1; +#endif + +struct lpt_softc { + unsigned short lp_unit; + + struct ppb_device lp_dev; + + struct ifnet sc_if; + u_char *sc_ifbuf; + int sc_iferrs; +}; + +static int nlp = 0; +#define MAXPLIP 8 /* XXX not much better! */ +static struct lpt_softc *lpdata[MAXPLIP]; + + +/* Tables for the lp# interface */ +static u_char *txmith; +#define txmitl (txmith+(1*LPIPTBLSIZE)) +#define trecvh (txmith+(2*LPIPTBLSIZE)) +#define trecvl (txmith+(3*LPIPTBLSIZE)) + +static u_char *ctxmith; +#define ctxmitl (ctxmith+(1*LPIPTBLSIZE)) +#define ctrecvh (ctxmith+(2*LPIPTBLSIZE)) +#define ctrecvl (ctxmith+(3*LPIPTBLSIZE)) + +/* Functions for the lp# interface */ +static struct ppb_device *lpprobe(struct ppb_data *); +static int lpattach(struct ppb_device *); + +static int lpinittables(void); +static int lpioctl(struct ifnet *, u_long, caddr_t); +static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +static void lpintr(int); + +/* + * Make ourselves visible as a ppbus driver + */ + +static struct ppb_driver lpdriver = { + lpprobe, lpattach, "lp" +}; +DATA_SET(ppbdriver_set, lpdriver); + + +/* + * lpprobe() + */ +static struct ppb_device * +lpprobe(struct ppb_data *ppb) +{ + struct lpt_softc *lp; + + /* if we haven't interrupts, the probe fails */ + if (!ppb->ppb_link->id_irq) + return (0); + + lp = (struct lpt_softc *) malloc(sizeof(struct lpt_softc), + M_TEMP, M_NOWAIT); + if (!lp) { + printf("lp: cannot malloc!\n"); + return (0); + } + bzero(lp, sizeof(struct lpt_softc)); + + lpdata[nlp] = lp; + + /* + * lp dependent initialisation. + */ + lp->lp_unit = nlp; + + if (bootverbose) + printf("plip: irq %d", ppb->ppb_link->id_irq); + + /* + * ppbus dependent initialisation. + */ + lp->lp_dev.id_unit = lp->lp_unit; + lp->lp_dev.name = lpdriver.name; + lp->lp_dev.ppb = ppb; + lp->lp_dev.intr = lpintr; + + /* Ok, go to next device on next probe */ + nlp ++; + + return (&lp->lp_dev); +} + +static int +lpattach (struct ppb_device *dev) +{ + int unit = dev->id_unit; + struct lpt_softc *sc = lpdata[unit]; + struct ifnet *ifp = &sc->sc_if; + + /* + * Report ourselves + */ + printf("plip%d: <PLIP network interface> on ppbus %d\n", + dev->id_unit, dev->ppb->ppb_link->adapter_unit); + + ifp->if_softc = sc; + ifp->if_name = "lp"; + ifp->if_unit = unit; + ifp->if_mtu = LPMTU; + ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; + ifp->if_ioctl = lpioctl; + ifp->if_output = lpoutput; + ifp->if_type = IFT_PARA; + ifp->if_hdrlen = 0; + ifp->if_addrlen = 0; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + if_attach(ifp); + +#if NBPFILTER > 0 + bpfattach(ifp, DLT_NULL, LPIPHDRLEN); +#endif + + return (1); +} +/* + * Build the translation tables for the LPIP (BSD unix) protocol. + * We don't want to calculate these nasties in our tight loop, so we + * precalculate them when we initialize. + */ +static int +lpinittables (void) +{ + int i; + + if (!txmith) + txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); + + if (!txmith) + return 1; + + if (!ctxmith) + ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT); + + if (!ctxmith) + return 1; + + for (i=0; i < LPIPTBLSIZE; i++) { + ctxmith[i] = (i & 0xF0) >> 4; + ctxmitl[i] = 0x10 | (i & 0x0F); + ctrecvh[i] = (i & 0x78) << 1; + ctrecvl[i] = (i & 0x78) >> 3; + } + + for (i=0; i < LPIPTBLSIZE; i++) { + txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; + txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); + trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); + trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); + } + + return 0; +} + +/* + * Process an ioctl request. + */ + +static int +lpioctl (struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct lpt_softc *sc = lpdata[ifp->if_unit]; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + u_char *ptr; + int error; + + switch (cmd) { + + case SIOCSIFDSTADDR: + case SIOCAIFADDR: + case SIOCSIFADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + return EAFNOSUPPORT; + + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) { + + ppb_wctr(&sc->lp_dev, 0x00); + ifp->if_flags &= ~IFF_RUNNING; + + /* IFF_UP is not set, try to release the bus anyway */ + ppb_release_bus(&sc->lp_dev); + break; + } + if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) { + + /* + * Try to allocate the ppbus as soon as possible + * With ppbus allocation, interrupts are enabled + * Now IFF_UP means that we own the bus + * + * XXX + * Should the request be interruptible? + */ + if ((error = ppb_request_bus(&sc->lp_dev, PPB_WAIT|PPB_INTR))) + return (error); + + if (lpinittables()) { + ppb_release_bus(&sc->lp_dev); + return ENOBUFS; + } + + sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN, + M_DEVBUF, M_WAITOK); + if (!sc->sc_ifbuf) { + ppb_release_bus(&sc->lp_dev); + return ENOBUFS; + } + + ppb_wctr(&sc->lp_dev, LPC_ENA); + ifp->if_flags |= IFF_RUNNING; + } + break; + + case SIOCSIFMTU: + ptr = sc->sc_ifbuf; + sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT); + if (!sc->sc_ifbuf) { + sc->sc_ifbuf = ptr; + return ENOBUFS; + } + if (ptr) + free(ptr,M_DEVBUF); + sc->sc_if.if_mtu = ifr->ifr_mtu; + break; + + case SIOCGIFMTU: + ifr->ifr_mtu = sc->sc_if.if_mtu; + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifr == 0) { + return EAFNOSUPPORT; /* XXX */ + } + switch (ifr->ifr_addr.sa_family) { + + case AF_INET: + break; + + default: + return EAFNOSUPPORT; + } + break; + + default: + lprintf("LP:ioctl(0x%x)\n",cmd); + return EINVAL; + } + return 0; +} + +static __inline int +clpoutbyte (u_char byte, int spin, struct ppb_device *dev) +{ + ppb_wdtr(dev, ctxmitl[byte]); + while (ppb_rstr(dev) & CLPIP_SHAKE) + if (--spin == 0) { + return 1; + } + ppb_wdtr(dev, ctxmith[byte]); + while (!(ppb_rstr(dev) & CLPIP_SHAKE)) + if (--spin == 0) { + return 1; + } + return 0; +} + +static __inline int +clpinbyte (int spin, struct ppb_device *dev) +{ + int c, cl; + + while((ppb_rstr(dev) & CLPIP_SHAKE)) + if(!--spin) { + return -1; + } + cl = ppb_rstr(dev); + ppb_wdtr(dev, 0x10); + + while(!(ppb_rstr(dev) & CLPIP_SHAKE)) + if(!--spin) { + return -1; + } + c = ppb_rstr(dev); + ppb_wdtr(dev, 0x00); + + return (ctrecvl[cl] | ctrecvh[c]); +} + +static void +lpintr (int unit) +{ + struct lpt_softc *sc = lpdata[unit]; + int len, s, j; + u_char *bp; + u_char c, cl; + struct mbuf *top; + + s = splhigh(); + + if (sc->sc_if.if_flags & IFF_LINK0) { + + /* Ack. the request */ + ppb_wdtr(&sc->lp_dev, 0x01); + + /* Get the packet length */ + j = clpinbyte(LPMAXSPIN2, &sc->lp_dev); + if (j == -1) + goto err; + len = j; + j = clpinbyte(LPMAXSPIN2, &sc->lp_dev); + if (j == -1) + goto err; + len = len + (j << 8); + if (len > sc->sc_if.if_mtu + MLPIPHDRLEN) + goto err; + + bp = sc->sc_ifbuf; + + while (len--) { + j = clpinbyte(LPMAXSPIN2, &sc->lp_dev); + if (j == -1) { + goto err; + } + *bp++ = j; + } + /* Get and ignore checksum */ + j = clpinbyte(LPMAXSPIN2, &sc->lp_dev); + if (j == -1) { + goto err; + } + + len = bp - sc->sc_ifbuf; + if (len <= CLPIPHDRLEN) + goto err; + + sc->sc_iferrs = 0; + + if (IF_QFULL(&ipintrq)) { + lprintf("DROP"); + IF_DROP(&ipintrq); + goto done; + } + len -= CLPIPHDRLEN; + sc->sc_if.if_ipackets++; + sc->sc_if.if_ibytes += len; + top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if, 0); + if (top) { + IF_ENQUEUE(&ipintrq, top); + schednetisr(NETISR_IP); + } + goto done; + } + while ((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE)) { + len = sc->sc_if.if_mtu + LPIPHDRLEN; + bp = sc->sc_ifbuf; + while (len--) { + + cl = ppb_rstr(&sc->lp_dev); + ppb_wdtr(&sc->lp_dev, 8); + + j = LPMAXSPIN2; + while((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE)) + if(!--j) goto err; + + c = ppb_rstr(&sc->lp_dev); + ppb_wdtr(&sc->lp_dev, 0); + + *bp++= trecvh[cl] | trecvl[c]; + + j = LPMAXSPIN2; + while (!((cl=ppb_rstr(&sc->lp_dev)) & LPIP_SHAKE)) { + if (cl != c && + (((cl = ppb_rstr(&sc->lp_dev)) ^ 0xb8) & 0xf8) == + (c & 0xf8)) + goto end; + if (!--j) goto err; + } + } + + end: + len = bp - sc->sc_ifbuf; + if (len <= LPIPHDRLEN) + goto err; + + sc->sc_iferrs = 0; + + if (IF_QFULL(&ipintrq)) { + lprintf("DROP"); + IF_DROP(&ipintrq); + goto done; + } +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf) { + bpf_tap(&sc->sc_if, sc->sc_ifbuf, len); + } +#endif + len -= LPIPHDRLEN; + sc->sc_if.if_ipackets++; + sc->sc_if.if_ibytes += len; + top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if, 0); + if (top) { + IF_ENQUEUE(&ipintrq, top); + schednetisr(NETISR_IP); + } + } + goto done; + + err: + ppb_wdtr(&sc->lp_dev, 0); + lprintf("R"); + sc->sc_if.if_ierrors++; + sc->sc_iferrs++; + + /* + * We are not able to send receive anything for now, + * so stop wasting our time + */ + if (sc->sc_iferrs > LPMAXERRS) { + printf("lp%d: Too many errors, Going off-line.\n", unit); + ppb_wctr(&sc->lp_dev, 0x00); + sc->sc_if.if_flags &= ~IFF_RUNNING; + sc->sc_iferrs=0; + } + + done: + splx(s); + return; +} + +static __inline int +lpoutbyte (u_char byte, int spin, struct ppb_device *dev) +{ + ppb_wdtr(dev, txmith[byte]); + while (!(ppb_rstr(dev) & LPIP_SHAKE)) + if (--spin == 0) + return 1; + ppb_wdtr(dev, txmitl[byte]); + while (ppb_rstr(dev) & LPIP_SHAKE) + if (--spin == 0) + return 1; + return 0; +} + +static int +lpoutput (struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt) +{ + struct lpt_softc *sc = lpdata[ifp->if_unit]; + int s, err; + struct mbuf *mm; + u_char *cp = "\0\0"; + u_char chksum = 0; + int count = 0; + int i; + int spin; + + /* We need a sensible value if we abort */ + cp++; + ifp->if_flags |= IFF_RUNNING; + + err = 1; /* assume we're aborting because of an error */ + + s = splhigh(); + + /* Suspend (on laptops) or receive-errors might have taken us offline */ + ppb_wctr(&sc->lp_dev, LPC_ENA); + + if (ifp->if_flags & IFF_LINK0) { + + if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) { + lprintf("&"); + lpintr(ifp->if_unit); + } + + /* Alert other end to pending packet */ + spin = LPMAXSPIN1; + ppb_wdtr(&sc->lp_dev, 0x08); + while ((ppb_rstr(&sc->lp_dev) & 0x08) == 0) + if (--spin == 0) { + goto nend; + } + + /* Calculate length of packet, then send that */ + + count += 14; /* Ethernet header len */ + + mm = m; + for (mm = m; mm; mm = mm->m_next) { + count += mm->m_len; + } + if (clpoutbyte(count & 0xFF, LPMAXSPIN1, &sc->lp_dev)) + goto nend; + if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, &sc->lp_dev)) + goto nend; + + /* Send dummy ethernet header */ + for (i = 0; i < 12; i++) { + if (clpoutbyte(i, LPMAXSPIN1, &sc->lp_dev)) + goto nend; + chksum += i; + } + + if (clpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev)) + goto nend; + if (clpoutbyte(0x00, LPMAXSPIN1, &sc->lp_dev)) + goto nend; + chksum += 0x08 + 0x00; /* Add into checksum */ + + mm = m; + do { + cp = mtod(mm, u_char *); + while (mm->m_len--) { + chksum += *cp; + if (clpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev)) + goto nend; + } + } while ((mm = mm->m_next)); + + /* Send checksum */ + if (clpoutbyte(chksum, LPMAXSPIN2, &sc->lp_dev)) + goto nend; + + /* Go quiescent */ + ppb_wdtr(&sc->lp_dev, 0); + + err = 0; /* No errors */ + + nend: + if (err) { /* if we didn't timeout... */ + ifp->if_oerrors++; + lprintf("X"); + } else { + ifp->if_opackets++; + ifp->if_obytes += m->m_pkthdr.len; + } + + m_freem(m); + + if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) { + lprintf("^"); + lpintr(ifp->if_unit); + } + (void) splx(s); + return 0; + } + + if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) { + lprintf("&"); + lpintr(ifp->if_unit); + } + + if (lpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev)) + goto end; + if (lpoutbyte(0x00, LPMAXSPIN2, &sc->lp_dev)) + goto end; + + mm = m; + do { + cp = mtod(mm,u_char *); + while (mm->m_len--) + if (lpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev)) + goto end; + } while ((mm = mm->m_next)); + + err = 0; /* no errors were encountered */ + + end: + --cp; + ppb_wdtr(&sc->lp_dev, txmitl[*cp] ^ 0x17); + + if (err) { /* if we didn't timeout... */ + ifp->if_oerrors++; + lprintf("X"); + } else { + ifp->if_opackets++; + ifp->if_obytes += m->m_pkthdr.len; +#if NBPFILTER > 0 + if (ifp->if_bpf) { + /* + * We need to prepend the packet type as + * a two byte field. Cons up a dummy header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer to it). + */ + struct mbuf m0; + u_short hdr = 0x800; + + m0.m_next = m; + m0.m_len = 2; + m0.m_data = (char *)&hdr; + + bpf_mtap(ifp, &m0); + } +#endif + } + + m_freem(m); + + if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) { + lprintf("^"); + lpintr(ifp->if_unit); + } + + (void) splx(s); + return 0; +} diff --git a/sys/dev/ppbus/nlpt.c b/sys/dev/ppbus/nlpt.c index 2915806..e855fae 100644 --- a/sys/dev/ppbus/nlpt.c +++ b/sys/dev/ppbus/nlpt.c @@ -47,7 +47,7 @@ * * from: unknown origin, 386BSD 0.1 * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp - * $Id: nlpt.c,v 1.7 1998/01/24 02:54:05 eivind Exp $ + * $Id: nlpt.c,v 1.8 1998/06/07 17:09:48 dfr Exp $ */ /* @@ -134,7 +134,7 @@ DATA_SET(ppbdriver_set, nlptdriver); #define OBUSY (1<<3) /* printer is busy doing output */ #define LPTOUT (1<<4) /* timeout while not selected */ #define TOUT (1<<5) /* timeout while not selected */ -#define INIT (1<<6) /* waiting to initialize for open */ +#define LPTINIT (1<<6) /* waiting to initialize for open */ #define INTERRUPTED (1<<7) /* write call was interrupted */ #define HAVEBUS (1<<8) /* the driver owns the bus */ @@ -168,15 +168,25 @@ static struct cdevsw nlpt_cdevsw = static int lpt_request_ppbus(struct lpt_data *sc, int how) { - sc->sc_state |= HAVEBUS; - return (ppb_request_bus(&sc->lpt_dev, how)); + int error; + + /* we have the bus only if the request succeded */ + if ((error = ppb_request_bus(&sc->lpt_dev, how)) == 0) + sc->sc_state |= HAVEBUS; + + return (error); } static int lpt_release_ppbus(struct lpt_data *sc) { - sc->sc_state &= ~HAVEBUS; - return (ppb_release_bus(&sc->lpt_dev)); + int error; + + /* we do not have the bus only if the request succeded */ + if ((error = ppb_release_bus(&sc->lpt_dev)) == 0) + sc->sc_state &= ~HAVEBUS; + + return (error); } /* @@ -316,6 +326,7 @@ nlptprobe(struct ppb_data *ppb) * ppbus dependent initialisation. */ sc->lpt_dev.id_unit = sc->lpt_unit; + sc->lpt_dev.name = nlptdriver.name; sc->lpt_dev.ppb = ppb; sc->lpt_dev.intr = nlptintr; @@ -401,19 +412,6 @@ nlptout(void *arg) * Avoid possible hangs do to missed interrupts */ if (sc->sc_xfercnt) { - /* if we cannot allocate the bus NOW, retry later */ - if ((sc->sc_state & HAVEBUS) == 0 && - lpt_request_ppbus (sc, PPB_DONTWAIT)) { - - sc->sc_backoff++; - if (sc->sc_backoff > hz/LPTOUTMAX) - sc->sc_backoff = - sc->sc_backoff > hz/LPTOUTMAX; - timeout(nlptout, (caddr_t)sc, - sc->sc_backoff); - return; - } - pl = spltty(); nlptintr(sc->lpt_unit); splx(pl); @@ -447,7 +445,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) nlprintf(LPT_NAME ": still open %x\n", sc->sc_state); return(EBUSY); } else - sc->sc_state |= INIT; + sc->sc_state |= LPTINIT; sc->sc_flags = LPTFLAGS(minor(dev)); @@ -457,7 +455,9 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) return(0); } - if (lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) + /* request the ppbus only if we don't have it already */ + if ((sc->sc_state & HAVEBUS) == 0 && + lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) return (EINTR); s = spltty(); @@ -523,7 +523,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) sc->sc_xfercnt = 0; splx(s); - /* release the bus, nlptout() will try to allocate it later */ + /* release the ppbus */ lpt_release_ppbus(sc); /* only use timeout if using interrupt */ @@ -572,9 +572,10 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p) ppb_wctr(&sc->lpt_dev, LPC_NINIT); brelse(sc->sc_inbuf); +end_close: + /* release the bus anyway */ lpt_release_ppbus(sc); -end_close: sc->sc_state = 0; sc->sc_xfercnt = 0; nlprintf("closed.\n"); @@ -660,7 +661,9 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag) return(EPERM); } - if (lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) + /* request the ppbus only if we don't have it already */ + if ((sc->sc_state & HAVEBUS) == 0 && + lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) return (EINTR); sc->sc_state &= ~INTERRUPTED; @@ -691,12 +694,15 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag) nlprintf("p"); err = nlpt_pushbytes(sc); - lpt_release_ppbus(sc); if (err) return(err); } } + + /* we have not been interrupted, release the ppbus */ + lpt_release_ppbus(sc); + return(0); } @@ -713,6 +719,10 @@ nlptintr(int unit) struct lpt_data *sc = lptdata[unit]; int sts; int i; + + /* we must own the bus to use it */ + if ((sc->sc_state & HAVEBUS) == 0) + return; /* * Is printer online and ready for output? @@ -744,7 +754,6 @@ nlptintr(int unit) * Wakeup is not done if write call was interrupted. */ sc->sc_state &= ~OBUSY; - lpt_release_ppbus(sc); if(!(sc->sc_state & INTERRUPTED)) wakeup((caddr_t)sc); diff --git a/sys/dev/ppbus/ppb_1284.c b/sys/dev/ppbus/ppb_1284.c index d1ab1c5..0a0ddeb 100644 --- a/sys/dev/ppbus/ppb_1284.c +++ b/sys/dev/ppbus/ppb_1284.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_1284.c,v 1.2 1997/09/01 00:51:44 bde Exp $ + * $Id: ppb_1284.c,v 1.3 1998/01/31 07:23:06 eivind Exp $ * */ @@ -38,14 +38,23 @@ #include <dev/ppbus/ppb_1284.h> /* - * nibble_1284_wait() + * do_1284_wait() * * Wait for the peripherial up to 40ms */ int -nibble_1284_wait(struct ppb_device *dev, char mask, char status) +do_1284_wait(struct ppb_device *dev, char mask, char status) { int i; + char r; + + /* try up to 5ms */ + for (i = 0; i < 20; i++) { + r = ppb_rstr(dev); + DELAY(25); + if ((r & mask) == status) + return (0); + } return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR)); } @@ -53,26 +62,55 @@ nibble_1284_wait(struct ppb_device *dev, char mask, char status) #define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4) /* + * byte_1284_inbyte() + * + * Read 1 byte in BYTE mode + */ +int +byte_1284_inbyte(struct ppb_device *dev, char *buffer) +{ + int error; + + /* notify the peripherial to put data on the lines */ + ppb_wctr(dev, PCD | AUTOFEED | nSTROBE | nINIT | nSELECTIN); + + /* wait for valid byte signal */ + if ((error = do_1284_wait(dev, nACK, 0))) + return (error); + + /* fetch data */ + *buffer = ppb_rdtr(dev); + + /* indicate that data has been received, not ready for another */ + ppb_wctr(dev, PCD | nAUTOFEED | nSTROBE | nINIT | nSELECTIN); + + /* wait peripherial's acknowledgement */ + if ((error = do_1284_wait(dev, nACK, nACK))) + return (error); + + /* acknowledge the peripherial */ + ppb_wctr(dev, PCD | nAUTOFEED | STROBE | nINIT | nSELECTIN); + + return (0); +} + +/* * nibble_1284_inbyte() * - * Read data in NIBBLE mode + * Read 1 byte in NIBBLE mode */ int nibble_1284_inbyte(struct ppb_device *dev, char *buffer) { - char nibble[2], r; + char nibble[2]; int i, error; - r = ppb_rctr(dev); - for (i = 0; i < 2; i++) { /* ready to take data (nAUTO low) */ - ppb_wctr(dev, r | AUTOFEED); + ppb_wctr(dev, AUTOFEED | nSTROBE | nINIT | nSELECTIN); - if ((error = nibble_1284_wait(dev, nACK, 0))) { - ppb_wctr(dev, r); + if ((error = do_1284_wait(dev, nACK, 0))) return (error); - } /* read nibble */ nibble[i] = ppb_rstr(dev); @@ -82,13 +120,11 @@ nibble_1284_inbyte(struct ppb_device *dev, char *buffer) #endif /* ack, not ready for another nibble */ - ppb_wctr(dev, r & ~AUTOFEED); + ppb_wctr(dev, nAUTOFEED | nSTROBE | nINIT | nSELECTIN); /* wait ack from peripherial */ - if ((error = nibble_1284_wait(dev, nACK, nACK))) { - ppb_wctr(dev, r); + if ((error = do_1284_wait(dev, nACK, nACK))) return (error); - } } *buffer = ((nibble2char(nibble[1]) << 4) & 0xf0) | @@ -112,11 +148,11 @@ nibble_1284_sync(struct ppb_device *dev) ctr = ppb_rctr(dev); ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN); - if (nibble_1284_wait(dev, nACK, 0)) + if (do_1284_wait(dev, nACK, 0)) return; ppb_wctr(dev, ctr | AUTOFEED); - nibble_1284_wait(dev, nACK, nACK); + do_1284_wait(dev, nACK, nACK); ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN); @@ -140,7 +176,7 @@ nibble_1284_mode(struct ppb_device *dev, int mode) DELAY(5); ppb_wctr(dev, (ctrl & ~SELECTIN) | AUTOFEED); - if ((error = nibble_1284_wait(dev, nACK | ERROR | SELECT | nFAULT, + if ((error = do_1284_wait(dev, nACK | ERROR | SELECT | nFAULT, ERROR | SELECT | nFAULT))) { ppb_wctr(dev, ctrl); return (error); diff --git a/sys/dev/ppbus/ppb_1284.h b/sys/dev/ppbus/ppb_1284.h index 527d90a..980fe68 100644 --- a/sys/dev/ppbus/ppb_1284.h +++ b/sys/dev/ppbus/ppb_1284.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: ppb_1284.h,v 1.1 1997/08/16 14:05:33 msmith Exp $ * */ #ifndef __1284_H @@ -32,9 +32,12 @@ #define NIBBLE_1284_NORMAL 0 #define NIBBLE_1284_REQUEST_ID 4 -extern void nibble_1284_sync(struct ppb_device *); +extern int do_1284_wait(struct ppb_device *, char, char); + +extern int byte_1284_inbyte(struct ppb_device *, char *); + extern int nibble_1284_inbyte(struct ppb_device *, char *); -extern int nibble_1284_wait(struct ppb_device *, char, char); +extern void nibble_1284_sync(struct ppb_device *); extern int nibble_1284_mode(struct ppb_device *, int); #endif diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c index 8c956be..6bea760 100644 --- a/sys/dev/ppbus/ppb_base.c +++ b/sys/dev/ppbus/ppb_base.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_base.c,v 1.2 1997/08/28 10:15:12 msmith Exp $ + * $Id: ppb_base.c,v 1.3 1997/09/01 00:51:45 bde Exp $ * */ #include <sys/param.h> @@ -98,78 +98,65 @@ ppb_poll_device(struct ppb_device *dev, int max, } /* - * ppb_reset_epp_timeout() + * ppb_set_mode() * - * Reset the EPP timeout bit in the status register. + * Set the operating mode of the chipset */ int -ppb_reset_epp_timeout(struct ppb_device *dev) +ppb_set_mode(struct ppb_device *dev, int mode) { struct ppb_data *ppb = dev->ppb; + int old_mode = ppb_get_mode(dev); - if (ppb->ppb_owner != dev) - return (EACCES); + if ((*ppb->ppb_link->adapter->setmode)(dev->id_unit, mode)) + return (-1); - (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit); + /* XXX yet device mode = ppbus mode = chipset mode */ + dev->mode = ppb->mode = mode; - return (0); + return (old_mode); } /* - * ppb_ecp_sync() + * ppb_reset_epp_timeout() * - * Wait for the ECP FIFO to be empty. + * Reset the EPP timeout bit in the status register */ int -ppb_ecp_sync(struct ppb_device *dev) +ppb_reset_epp_timeout(struct ppb_device *dev) { struct ppb_data *ppb = dev->ppb; if (ppb->ppb_owner != dev) return (EACCES); - (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit); + (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit); return (0); } /* - * ppb_get_mode() + * ppb_ecp_sync() * - * Read the mode (SPP, EPP...) of the chipset. + * Wait for the ECP FIFO to be empty */ int -ppb_get_mode(struct ppb_device *dev) +ppb_ecp_sync(struct ppb_device *dev) { - return (dev->ppb->ppb_link->mode); -} + struct ppb_data *ppb = dev->ppb; -/* - * ppb_get_epp_protocol() - * - * Read the EPP protocol (1.9 or 1.7). - */ -int -ppb_get_epp_protocol(struct ppb_device *dev) -{ - return (dev->ppb->ppb_link->epp_protocol); -} + if (ppb->ppb_owner != dev) + return (EACCES); -/* - * ppb_get_irq() - * - * Return the irq, 0 if none. - */ -int -ppb_get_irq(struct ppb_device *dev) -{ - return (dev->ppb->ppb_link->id_irq); + (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit); + + return (0); } /* * ppb_get_status() * - * Read the status register and update the status info. + * Read the status register and update the status info */ int ppb_get_status(struct ppb_device *dev, struct ppb_status *status) diff --git a/sys/dev/ppbus/ppb_msq.c b/sys/dev/ppbus/ppb_msq.c new file mode 100644 index 0000000..fff70a7 --- /dev/null +++ b/sys/dev/ppbus/ppb_msq.c @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 1998 Nicolas Souchu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ppb_msq.c,v 1.1.2.4 1998/06/16 23:35:51 son Exp $ + * + */ +#include <machine/stdarg.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> + +/* msq index (see PPB_MAX_XFER) + * These are device modes + */ +#define COMPAT_MSQ 0x0 +#define NIBBLE_MSQ 0x1 +#define PS2_MSQ 0x2 +#define EPP17_MSQ 0x3 +#define EPP19_MSQ 0x4 +#define ECP_MSQ 0x5 + +/* + * Device mode to submsq conversion + */ +static struct ppb_xfer * +mode2xfer(struct ppb_device *dev, int opcode) +{ + int index, epp; + struct ppb_xfer *table; + + switch (opcode) { + case MS_OP_GET: + table = dev->get_xfer; + break; + + case MS_OP_PUT: + table = dev->put_xfer; + break; + + default: + panic("%s: unknown opcode (%d)", __FUNCTION__, opcode); + } + + /* retrieve the device operating mode */ + switch (ppb_get_mode(dev)) { + case PPB_COMPATIBLE: + index = COMPAT_MSQ; + break; + case PPB_NIBBLE: + index = NIBBLE_MSQ; + break; + case PPB_PS2: + index = PS2_MSQ; + break; + case PPB_EPP: + switch ((epp = ppb_get_epp_protocol(dev))) { + case EPP_1_7: + index = EPP17_MSQ; + break; + case EPP_1_9: + index = EPP19_MSQ; + break; + default: + panic("%s: unknown EPP protocol (0x%x)!", __FUNCTION__, + epp); + } + break; + case PPB_ECP: + index = ECP_MSQ; + break; + default: + panic("%s: unknown mode (%d)", __FUNCTION__, dev->mode); + } + + return (&table[index]); +} + +/* + * ppb_MS_init() + * + * Initialize device dependent submicrosequence of the current mode + * + */ +int +ppb_MS_init(struct ppb_device *dev, struct ppb_microseq *loop, int opcode) +{ + struct ppb_xfer *xfer = mode2xfer(dev, opcode); + + xfer->loop = loop; + + return (0); +} + +/* + * ppb_MS_exec() + * + * Execute any microsequence opcode - expensive + * + */ +int +ppb_MS_exec(struct ppb_device *dev, int opcode, union ppb_insarg param1, + union ppb_insarg param2, union ppb_insarg param3, int *ret) +{ + struct ppb_microseq msq[] = { + { MS_UNKNOWN, { MS_UNKNOWN, MS_UNKNOWN, MS_UNKNOWN } }, + MS_RET(0) + }; + + /* initialize the corresponding microseq */ + msq[0].opcode = opcode; + msq[0].arg[0] = param1; + msq[0].arg[1] = param2; + msq[0].arg[2] = param3; + + /* execute the microseq */ + return (ppb_MS_microseq(dev, msq, ret)); +} + +/* + * ppb_MS_loop() + * + * Execute a microseq loop + * + */ +int +ppb_MS_loop(struct ppb_device *dev, struct ppb_microseq *prolog, + struct ppb_microseq *body, struct ppb_microseq *epilog, + int iter, int *ret) +{ + struct ppb_microseq loop_microseq[] = { + MS_CALL(NULL), /* execute prolog */ + + MS_SET(MS_UNKNOWN), /* set size of transfer */ + /* loop: */ + MS_CALL(NULL), /* execute body */ + MS_DBRA(-1 /* loop: */), + + MS_CALL(NULL), /* execute epilog */ + MS_RET(0) + }; + + /* initialize the structure */ + loop_microseq[0].arg[0].p = (void *)prolog; + loop_microseq[1].arg[0].i = iter; + loop_microseq[2].arg[0].p = (void *)body; + loop_microseq[4].arg[0].p = (void *)epilog; + + /* execute the loop */ + return (ppb_MS_microseq(dev, loop_microseq, ret)); +} + +/* + * ppb_MS_init_msq() + * + * Initialize a microsequence - see macros in ppb_msq.h + * + */ +int +ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...) +{ + int i; + int param, ins, arg, type; + va_list p_list = 0; + + va_start(p_list, nbparam); + + for (i=0; i<nbparam; i++) { + /* retrieve the parameter descriptor */ + param = va_arg(p_list, int); + + ins = MS_INS(param); + arg = MS_ARG(param); + type = MS_TYP(param); + + /* check the instruction position */ + if (arg >= PPB_MS_MAXARGS) + panic("%s: parameter out of range (0x%x)!", + __FUNCTION__, param); + +#if 0 + printf("%s: param = %d, ins = %d, arg = %d, type = %d\n", + __FUNCTION__, param, ins, arg, type); +#endif + + /* properly cast the parameter */ + switch (type) { + case MS_TYP_INT: + msq[ins].arg[arg].i = va_arg(p_list, int); + break; + + case MS_TYP_CHA: + msq[ins].arg[arg].c = va_arg(p_list, char); + break; + + case MS_TYP_PTR: + msq[ins].arg[arg].p = va_arg(p_list, void *); + break; + + case MS_TYP_FUN: + msq[ins].arg[arg].f = va_arg(p_list, void *); + break; + + default: + panic("%s: unknown parameter (0x%x)!", __FUNCTION__, + param); + } + } + + return (0); +} + +/* + * ppb_MS_microseq() + * + * Interprete a microsequence. Some microinstructions are executed at adapter + * level to avoid function call overhead between ppbus and the adapter + */ +int +ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret) +{ + struct ppb_data *ppb = dev->ppb; + struct ppb_microseq *mi; /* current microinstruction */ + int msq_index; + int pc, error; + + struct ppb_xfer *xfer; + + /* microsequence executed to initialize the transfer */ + struct ppb_microseq initxfer[] = { + MS_PTR(MS_UNKNOWN), /* set ptr to buffer */ + MS_SET(MS_UNKNOWN), /* set transfer size */ + MS_RET(0) + }; + + if (ppb->ppb_owner != dev) + return (EACCES); + +#define INCR_PC (pc ++) + + pc = 0; + for (;;) { + + /* retrieve the next microinstruction to execute */ + mi = &msq[pc]; + + switch (mi->opcode) { + case MS_OP_PUT: + case MS_OP_GET: + + /* attempt to choose the best mode for the device */ + xfer = mode2xfer(dev, mi->opcode); + + /* figure out if we should use ieee1284 code */ + if (!xfer->loop) + panic("%s: IEEE1284 code not supported", + __FUNCTION__); + + /* XXX should use ppb_MS_init_msq() */ + initxfer[0].arg[0].p = mi->arg[0].p; + initxfer[1].arg[0].i = mi->arg[1].i; + + /* initialize transfer */ + ppb_MS_microseq(dev, initxfer, &error); + + if (error) + goto error; + + /* the xfer microsequence should not contain any + * MS_OP_PUT or MS_OP_GET! + */ + ppb_MS_microseq(dev, xfer->loop, &error); + + if (error) + goto error; + + INCR_PC; + break; + + case MS_OP_RET: + *ret = mi->arg[0].i; /* return code */ + return (0); + break; + + default: + /* executing microinstructions at ppc level is + * faster. This is the default if the microinstr + * is unknown here + */ + if ((error = ppb->ppb_link->adapter->exec_microseq( + dev->id_unit, msq, &pc))) + return (error); + + break; + } + } +error: + *ret = error; + return (0); +} + diff --git a/sys/dev/ppbus/ppb_msq.h b/sys/dev/ppbus/ppb_msq.h new file mode 100644 index 0000000..71e41b2 --- /dev/null +++ b/sys/dev/ppbus/ppb_msq.h @@ -0,0 +1,190 @@ +/*- + * Copyright (c) 1998 Nicolas Souchu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ppb_msq.h,v 1.1.2.6 1998/06/17 00:37:12 son Exp $ + * + */ +#ifndef __PPB_MSQ_H +#define __PPB_MSQ_H + +/* + * Basic definitions + */ + +/* microsequence parameter descriptor */ +#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position */ +#define MS_ARG_MASK 0x0f00 /* mask to retrieve the argument number */ +#define MS_TYP_MASK 0xf000 /* mask to retrieve the type of the param */ + +/* offset of each mask (see above) */ +#define MS_INS_OFFSET 0 +#define MS_ARG_OFFSET 8 +#define MS_TYP_OFFSET 12 + +/* list of parameter types */ +#define MS_TYP_INT 0x0 /* integer */ +#define MS_TYP_CHA 0x1 /* character */ +#define MS_TYP_PTR 0x2 /* void pointer */ +#define MS_TYP_FUN 0x3 /* function pointer */ + +#define MS_PARAM(ins,arg,typ) \ + (((ins<<MS_INS_OFFSET) & MS_INS_MASK) | \ + ((arg<<MS_ARG_OFFSET) & MS_ARG_MASK) | \ + ((typ<<MS_TYP_OFFSET) & MS_TYP_MASK)) + +#define MS_INS(param) ((param & MS_INS_MASK) >> MS_INS_OFFSET) +#define MS_ARG(param) ((param & MS_ARG_MASK) >> MS_ARG_OFFSET) +#define MS_TYP(param) ((param & MS_TYP_MASK) >> MS_TYP_OFFSET) + +/* microsequence opcodes - do not change! */ +#define MS_OP_GET 0 /* get <ptr>, <len> */ +#define MS_OP_PUT 1 /* put <ptr>, <len> */ + +#define MS_OP_RFETCH 2 /* rfetch <reg>, <mask>, <ptr> */ +#define MS_OP_RSET 3 /* rset <reg>, <mask>, <mask> */ +#define MS_OP_RASSERT 4 /* rassert <reg>, <mask> */ +#define MS_OP_DELAY 5 /* delay <val> */ +#define MS_OP_SET 6 /* set <val> */ +#define MS_OP_DBRA 7 /* dbra <offset> */ +#define MS_OP_BRSET 8 /* brset <mask>, <offset> */ +#define MS_OP_BRCLEAR 9 /* brclear <mask>, <offset> */ +#define MS_OP_RET 10 /* ret <retcode> */ +#define MS_OP_C_CALL 11 /* c_call <function>, <parameter> */ +#define MS_OP_PTR 12 /* ptr <pointer> */ +#define MS_RESERVED_1 13 /* reserved */ +#define MS_RESERVED_2 14 /* reserved */ +#define MS_OP_SUBRET 15 /* subret <code> */ +#define MS_OP_CALL 16 /* call <microsequence> */ +#define MS_OP_RASSERT_P 17 /* rassert_p <iter>, <reg> */ +#define MS_OP_RFETCH_P 18 /* rfetch_p <iter>, <reg>, <mask> */ +#define MS_OP_TRIG 19 /* trigger <reg>, <len>, <array> */ + +/* common masks */ +#define MS_CLEAR_ALL 0x0 +#define MS_ASSERT_NONE 0x0 +#define MS_ASSERT_ALL 0xff +#define MS_FETCH_ALL 0xff + +/* undefined parameter value */ +#define MS_UNKNOWN 0 + +/* these are register numbers according to our PC-like parallel port model */ +#define MS_REG_DTR 0x0 +#define MS_REG_STR 0x1 +#define MS_REG_CTR 0x2 +#define MS_REG_EPP 0x4 + +/* + * Microsequence macro abstraction level + */ + +/* register operations */ +#define MS_RSET(reg,assert,clear) { MS_OP_RSET, { reg, assert, clear} } +#define MS_RASSERT(reg,byte) { MS_OP_RASSERT, { reg, byte } } +#define MS_RCLR(reg,clear) { MS_OP_RSET, { reg, MS_ASSERT_NONE, clear } } + +#define MS_RFETCH(reg,mask,ptr) { MS_OP_RFETCH, { reg, mask, ptr } } + +/* trigger the port with array[char, delay,...] */ +#define MS_TRIG(reg,len,array) { MS_OP_TRIG, { reg, len, array } } + +/* assert/fetch from/to ptr */ +#define MS_RASSERT_P(n,reg) { MS_OP_RASSERT_P, { n, reg } } +#define MS_RFETCH_P(n,reg,mask) { MS_OP_RFETCH_P, { n, reg, mask } } + +/* ptr manipulation */ +#define MS_PTR(ptr) { MS_OP_PTR, { ptr } } + +#define MS_DASS(byte) MS_RASSERT(MS_REG_DTR,byte) +#define MS_SASS(byte) MS_RASSERT(MS_REG_STR,byte) +#define MS_CASS(byte) MS_RASSERT(MS_REG_CTR,byte) + +#define MS_SET(offset) { MS_OP_SET, { offset } } +#define MS_BRSET(mask,offset) { MS_OP_BRSET, { mask, offset } } +#define MS_DBRA(offset) { MS_OP_DBRA, { offset } } + +/* C function or submicrosequence call */ +#define MS_C_CALL(function,parameter) \ + { MS_OP_C_CALL, { function, parameter } } +#define MS_CALL(microseq) { MS_OP_CALL, { microseq } } + +/* mode dependent read/write operations + * ppb_MS_xxx_init() call required otherwise default is + * IEEE1284 operating mode */ +#define MS_PUT(ptr,len) { MS_OP_PUT, { ptr, len } } +#define MS_GET(ptr,len) { MS_OP_GET, { ptr, len } } + +/* delay in microseconds */ +#define MS_DELAY(delay) { MS_OP_DELAY, { delay } } + +/* return from submicrosequence execution or microseqence execution */ +#define MS_SUBRET(code) { MS_OP_SUBRET, { code } } +#define MS_RET(code) { MS_OP_RET, { code } } + +/* + * Function abstraction level + */ + +#define ppb_MS_GET_init(dev,body) ppb_MS_init(dev, body, MS_OP_GET) + +#define ppb_MS_PUT_init(dev,body) ppb_MS_init(dev, body, MS_OP_PUT) + +extern int ppb_MS_init( + struct ppb_device *, /* ppbus device */ + struct ppb_microseq *, /* loop msq to assign */ + int opcode /* MS_OP_GET, MS_OP_PUT */ + ); + +extern int ppb_MS_init_msq( + struct ppb_microseq *, + int, /* number of parameters */ + ... /* descriptor, value, ... */ + ); + +extern int ppb_MS_exec( + struct ppb_device *, /* ppbus device */ + int, /* microseq opcode */ + union ppb_insarg, /* param1 */ + union ppb_insarg, /* param2 */ + union ppb_insarg, /* param3 */ + int * /* returned value */ + ); + +extern int ppb_MS_loop( + struct ppb_device *, /* ppbus device */ + struct ppb_microseq *, /* prologue msq of loop */ + struct ppb_microseq *, /* body msq of loop */ + struct ppb_microseq *, /* epilogue msq of loop */ + int, /* number of iter */ + int * /* returned value */ + ); + +extern int ppb_MS_microseq( + struct ppb_device *, /* ppbus device */ + struct ppb_microseq *, /* msq to execute */ + int * /* returned value */ + ); + +#endif diff --git a/sys/dev/ppbus/ppbconf.c b/sys/dev/ppbus/ppbconf.c index 647676d..5756327 100644 --- a/sys/dev/ppbus/ppbconf.c +++ b/sys/dev/ppbus/ppbconf.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,7 +37,7 @@ #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_1284.h> -static LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */ +LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */ /* * Add a null driver so that the linker set always exists. @@ -53,7 +53,6 @@ DATA_SET(ppbdriver_set, nulldriver); * ppb_alloc_bus() * * Allocate area to store the ppbus description. - * This function is called by ppcattach(). */ struct ppb_data * ppb_alloc_bus(void) @@ -137,8 +136,8 @@ ppb_pnp_detect(struct ppb_data *ppb) { char *token, *q, *class = 0; int i, len, error; + int class_id = -1; char str[PPB_PnP_STRING_SIZE+1]; - struct ppb_device pnpdev; /* temporary device to perform I/O */ /* initialize the pnpdev structure for future use */ @@ -146,44 +145,48 @@ ppb_pnp_detect(struct ppb_data *ppb) pnpdev.ppb = ppb; -#ifdef PnP_DEBUG - printf("ppb: <PnP> probing PnP devices on ppbus%d...\n", - ppb->ppb_link->adapter_unit); -#endif + if (bootverbose) + printf("ppb: <PnP> probing devices on ppbus %d...\n", + ppb->ppb_link->adapter_unit); + + if (ppb_request_bus(&pnpdev, PPB_DONTWAIT)) { + if (bootverbose) + printf("ppb: <PnP> cannot allocate ppbus!\n"); + return (-1); + } ppb_wctr(&pnpdev, nINIT | SELECTIN); /* select NIBBLE_1284_REQUEST_ID mode */ if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) { -#ifdef PnP_DEBUG - printf("ppb: <PnP> nibble_1284_mode()=%d\n", error); -#endif - return (-1); + if (bootverbose) + printf("ppb: <PnP> nibble_1284_mode()=%d\n", error); + goto end_detect; } len = 0; for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) { if ((error = nibble_1284_inbyte(&pnpdev, q))) { -#ifdef PnP_DEBUG - printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", error); -#endif - return (-1); + if (bootverbose) + printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", + error); + goto end_detect; } if (len++ >= PPB_PnP_STRING_SIZE) { printf("ppb: <PnP> not space left!\n"); - return (-1); + goto end_detect; } } *q = '\0'; nibble_1284_sync(&pnpdev); -#ifdef PnP_DEBUG - printf("ppb: <PnP> %d characters: ", len); - for (i = 0; i < len; i++) - printf("0x%x ", str[i]); - printf("\n"); -#endif + if (bootverbose) { + printf("ppb: <PnP> %d characters: ", len); + for (i = 0; i < len; i++) + printf("0x%x ", str[i]); + printf("\n"); + } /* replace ';' characters by '\0' */ for (i = 0; i < len; i++) @@ -226,12 +229,16 @@ ppb_pnp_detect(struct ppb_data *ppb) /* identify class ident */ for (i = 0; pnp_tokens[i] != NULL; i++) { if (search_token(class, len, pnp_tokens[i]) != NULL) { - return (i); - break; + class_id = i; + goto end_detect; } } - return (PPB_PnP_UNKNOWN); + class_id = PPB_PnP_UNKNOWN; + +end_detect: + ppb_release_bus(&pnpdev); + return (class_id); } /* @@ -305,6 +312,24 @@ ppb_lookup_bus(int base_port) } /* + * ppb_lookup_link() + * + * Get ppb_data structure pointer according to the unit value + * of the corresponding link structure + */ +struct ppb_data * +ppb_lookup_link(int unit) +{ + struct ppb_data *ppb; + + for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next) + if (ppb->ppb_link->adapter_unit == unit) + break; + + return (ppb); +} + +/* * ppb_attach_device() * * Called by loadable kernel modules to add a device @@ -370,6 +395,15 @@ ppb_request_bus(struct ppb_device *dev, int how) } else { ppb->ppb_owner = dev; + /* restore the context of the device + * The first time, ctx.valid is certainly false + * then do not change anything. This is usefull for + * drivers that do not set there operating mode + * during attachement + */ + if (dev->ctx.valid) + ppb_set_mode(dev, dev->ctx.mode); + splx(s); return (0); } @@ -398,6 +432,12 @@ ppb_release_bus(struct ppb_device *dev) ppb->ppb_owner = 0; splx(s); + /* save the context of the device */ + dev->ctx.mode = ppb_get_mode(dev); + + /* ok, now the context of the device is valid */ + dev->ctx.valid = 1; + /* wakeup waiting processes */ wakeup(ppb); diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h index 5a69d85..fc723d4 100644 --- a/sys/dev/ppbus/ppbconf.h +++ b/sys/dev/ppbus/ppbconf.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppbconf.h,v 1.5 1997/09/01 18:39:37 bde Exp $ + * $Id: ppbconf.h,v 1.6 1998/06/07 19:44:21 phk Exp $ * */ #ifndef __PPBCONF_H @@ -37,21 +37,23 @@ #define PPBPRI PZERO+8 /* - * Parallel Port Chipset modes. + * Parallel Port Chipset mode masks. + * NIBBLE mode is supposed to be available under each other modes. */ -#define PPB_AUTODETECT 0x0 /* autodetect */ -#define PPB_NIBBLE 0x1 /* standard 4 bit mode */ +#define PPB_COMPATIBLE 0x0 /* Centronics compatible mode */ + +#define PPB_NIBBLE 0x1 /* reverse 4 bit mode */ #define PPB_PS2 0x2 /* PS/2 byte mode */ -#define PPB_EPP 0x3 /* EPP mode, 32 bit */ -#define PPB_ECP_EPP 0x4 /* ECP in EPP mode */ -#define PPB_ECP_PS2 0x5 /* ECP in PS/2 mode */ -#define PPB_ECP 0x6 /* ECP mode */ -#define PPB_UNKNOWN 0x7 /* the last one */ +#define PPB_EPP 0x4 /* EPP mode, 32 bit */ +#define PPB_ECP 0x8 /* ECP mode */ -#define PPB_IS_EPP(mode) (mode == PPB_EPP || mode == PPB_ECP_EPP) +#define PPB_SPP PPB_NIBBLE|PPB_PS2 +#define PPB_IS_EPP(mode) (mode & PPB_EPP) #define PPB_IN_EPP_MODE(dev) (PPB_IS_EPP (ppb_get_mode (dev))) +#define n(flags) (~(flags) & (flags)) + /* * Parallel Port Chipset control bits. */ @@ -62,6 +64,12 @@ #define IRQENABLE 0x10 #define PCD 0x20 +#define nSTROBE n(STROBE) +#define nAUTOFEED n(AUTOFEED) +#define INIT n(nINIT) +#define nSELECTIN n(SELECTIN) +#define nPCD n(PCD) + /* * Parallel Port Chipset status bits. */ @@ -87,21 +95,74 @@ struct ppb_status { }; /* - * How tsleep () is called in ppb_request_bus (). + * How tsleep() is called in ppb_request_bus(). */ #define PPB_DONTWAIT 0 #define PPB_NOINTR 0 #define PPB_WAIT 0x1 #define PPB_INTR 0x2 -struct ppb_data; /* see below */ +/* + * Microsequence stuff. + */ +#define PPB_MS_MAXLEN 64 /* XXX according to MS_INS_MASK */ +#define PPB_MS_MAXARGS 3 /* according to MS_ARG_MASK */ + +/* maximum number of mode dependent + * submicrosequences for in/out operations + */ +#define PPB_MAX_XFER 6 + +union ppb_insarg { + int i; + char c; + void *p; + int (* f)(void *, char *); +}; + +struct ppb_microseq { + int opcode; /* microins. opcode */ + union ppb_insarg arg[PPB_MS_MAXARGS]; /* arguments */ +}; + +/* microseqences used for GET/PUT operations */ +struct ppb_xfer { + struct ppb_microseq *loop; /* the loop microsequence */ +}; /* * Parallel Port Bus Device structure. */ +struct ppb_data; /* see below */ + +struct ppb_context { + int valid; /* 1 if the struct is valid */ + int mode; /* XXX chipset operating mode */ + + struct microseq *curpc; /* pc in curmsq */ + struct microseq *curmsq; /* currently executed microseqence */ +}; + + struct ppb_device { int id_unit; /* unit of the device */ + char *name; /* name of the device */ + + ushort mode; /* current mode of the device */ + ushort avm; /* available modes of the device */ + + struct ppb_context ctx; /* context of the device */ + + /* mode dependent get msq. If NULL, + * IEEE1284 code is used */ + struct ppb_xfer + get_xfer[PPB_MAX_XFER]; + + /* mode dependent put msq. If NULL, + * IEEE1284 code is used */ + struct ppb_xfer + put_xfer[PPB_MAX_XFER]; void (*intr)(int); /* interrupt handler */ @@ -119,6 +180,10 @@ struct ppb_adapter { void (*reset_epp_timeout)(int); void (*ecp_sync)(int); + int (*exec_microseq)(int, struct ppb_microseq *, int *); + + int (*setmode)(int, int); + void (*outsb_epp)(int, char *, int); void (*outsw_epp)(int, char *, int); void (*outsl_epp)(int, char *, int); @@ -147,10 +212,8 @@ struct ppb_adapter { struct ppb_link { int adapter_unit; /* unit of the adapter */ - int base; /* base address of the port */ int id_irq; /* != 0 if irq enabled */ - int mode; /* NIBBLE, PS2, EPP, ECP */ #define EPP_1_9 0x0 /* default */ #define EPP_1_7 0x1 @@ -164,7 +227,7 @@ struct ppb_link { /* * Maximum size of the PnP info string */ -#define PPB_PnP_STRING_SIZE 160 /* XXX */ +#define PPB_PnP_STRING_SIZE 128 /* XXX */ /* * Parallel Port Bus structure. @@ -184,6 +247,11 @@ struct ppb_data { #define PPB_PnP_UNKNOWN 10 int class_id; /* not a PnP device if class_id < 0 */ + ushort mode; /* IEEE 1284-1994 mode + * NIBBLE, PS2, EPP or ECP */ + ushort avm; /* IEEE 1284-1994 available + * modes */ + struct ppb_link *ppb_link; /* link to the adapter */ struct ppb_device *ppb_owner; /* device which owns the bus */ LIST_HEAD(, ppb_device) ppb_devs; /* list of devices on the bus */ @@ -205,6 +273,7 @@ extern struct linker_set ppbdriver_set; extern struct ppb_data *ppb_alloc_bus(void); extern struct ppb_data *ppb_next_bus(struct ppb_data *); extern struct ppb_data *ppb_lookup_bus(int); +extern struct ppb_data *ppb_lookup_link(int); extern int ppb_attach_device(struct ppb_device *); extern void ppb_remove_device(struct ppb_device *); @@ -220,56 +289,61 @@ extern int ppb_poll_device(struct ppb_device *, int, char, char, int); extern int ppb_reset_epp_timeout(struct ppb_device *); extern int ppb_ecp_sync(struct ppb_device *); extern int ppb_get_status(struct ppb_device *, struct ppb_status *); -extern int ppb_get_mode(struct ppb_device *); -extern int ppb_get_epp_protocol(struct ppb_device *); -extern int ppb_get_irq(struct ppb_device *); + +extern int ppb_set_mode(struct ppb_device *, int); /* * These are defined as macros for speedup. */ -#define ppb_outsb_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->outsb_epp) \ +#define ppb_get_base_addr(dev) ((dev)->ppb->ppb_link->base) +#define ppb_get_epp_protocol(dev) ((dev)->ppb->ppb_link->epp_protocol) +#define ppb_get_irq(dev) ((dev)->ppb->ppb_link->id_irq) + +#define ppb_get_mode(dev) ((dev)->mode) + +#define ppb_outsb_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->outsb_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_outsw_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->outsw_epp) \ +#define ppb_outsw_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->outsw_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_outsl_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->outsl_epp) \ +#define ppb_outsl_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->outsl_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_insb_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->insb_epp) \ +#define ppb_insb_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->insb_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_insw_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->insw_epp) \ +#define ppb_insw_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->insw_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_insl_epp(dev,buf,cnt) \ - (*(dev)->ppb->ppb_link->adapter->insl_epp) \ +#define ppb_insl_epp(dev,buf,cnt) \ + (*(dev)->ppb->ppb_link->adapter->insl_epp) \ ((dev)->ppb->ppb_link->adapter_unit, buf, cnt) -#define ppb_rdtr(dev) (*(dev)->ppb->ppb_link->adapter->r_dtr) \ - ((dev)->ppb->ppb_link->adapter_unit) -#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \ - ((dev)->ppb->ppb_link->adapter_unit) -#define ppb_rctr(dev) (*(dev)->ppb->ppb_link->adapter->r_ctr) \ +#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \ ((dev)->ppb->ppb_link->adapter_unit) -#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \ +#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \ ((dev)->ppb->ppb_link->adapter_unit) -#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \ +#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \ ((dev)->ppb->ppb_link->adapter_unit) -#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \ - ((dev)->ppb->ppb_link->adapter_unit) - -#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \ +#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \ ((dev)->ppb->ppb_link->adapter_unit, byte) -#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \ +#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \ ((dev)->ppb->ppb_link->adapter_unit, byte) -#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \ +#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \ ((dev)->ppb->ppb_link->adapter_unit, byte) -#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \ + +#define ppb_rdtr(dev) (*(dev)->ppb->ppb_link->adapter->r_dtr) \ + ((dev)->ppb->ppb_link->adapter_unit) +#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \ + ((dev)->ppb->ppb_link->adapter_unit) +#define ppb_rctr(dev) (*(dev)->ppb->ppb_link->adapter->r_ctr) \ + ((dev)->ppb->ppb_link->adapter_unit) +#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \ ((dev)->ppb->ppb_link->adapter_unit, byte) -#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \ +#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \ ((dev)->ppb->ppb_link->adapter_unit, byte) -#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \ +#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \ ((dev)->ppb->ppb_link->adapter_unit, byte) #endif diff --git a/sys/dev/ppbus/pps.c b/sys/dev/ppbus/pps.c index fa975d2..e39eb14 100644 --- a/sys/dev/ppbus/pps.c +++ b/sys/dev/ppbus/pps.c @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - * $Id: pps.c,v 1.8 1998/06/13 09:30:10 phk Exp $ + * $Id: pps.c,v 1.9 1998/06/21 18:02:32 bde Exp $ * * This driver implements a draft-mogul-pps-api-02.txt PPS source. * @@ -93,6 +93,7 @@ ppsprobe(struct ppb_data *ppb) sc->pps_dev.id_unit = sc->pps_unit; sc->pps_dev.ppb = ppb; + sc->pps_dev.name = ppsdriver.name; sc->pps_dev.intr = ppsintr; return (&sc->pps_dev); diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c index 44cb54d..aa5779a 100644 --- a/sys/dev/ppbus/vpo.c +++ b/sys/dev/ppbus/vpo.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,39 +44,42 @@ #endif /*KERNEL */ #include <dev/ppbus/ppbconf.h> -#include <dev/ppbus/vpo.h> +#include <dev/ppbus/vpoio.h> -/* -------------------------------------------------------------------- - * HERE ARE THINGS YOU MAY HAVE/WANT TO CHANGE - */ +#define VP0_BUFFER_SIZE 0x12000 -/* - * XXX - * We may add a timeout queue to avoid active polling on nACK. - */ -#define VP0_SELTMO 5000 /* select timeout */ -#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ -#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ +struct vpo_sense { + struct scsi_sense cmd; + unsigned int stat; + unsigned int count; +}; -/* - * DO NOT MODIFY ANYTHING UNDER THIS LINE - * -------------------------------------------------------------------- - */ +struct vpo_data { + unsigned short vpo_unit; + + int vpo_stat; + int vpo_count; + int vpo_error; + + struct ppb_status vpo_status; + struct vpo_sense vpo_sense; + + unsigned char vpo_buffer[VP0_BUFFER_SIZE]; + + struct vpoio_data vpo_io; /* interface to low level functions */ + + struct scsi_link sc_link; +}; -static __inline int vpoio_do_scsi(struct vpo_data *, int, int, char *, int, - char *, int, int *, int *); static int32_t vpo_scsi_cmd(struct scsi_xfer *); static void vpominphys(struct buf *); static u_int32_t vpo_adapter_info(int); -static int vpo_detect(struct vpo_data *vpo); - static int nvpo = 0; #define MAXVP0 8 /* XXX not much better! */ static struct vpo_data *vpodata[MAXVP0]; -#ifdef KERNEL static struct scsi_adapter vpo_switch = { vpo_scsi_cmd, @@ -117,8 +120,6 @@ static struct ppb_driver vpodriver = { DATA_SET(ppbdriver_set, vpodriver); -#endif /* KERNEL */ - static u_int32_t vpo_adapter_info(int unit) { @@ -134,8 +135,8 @@ vpo_adapter_info(int unit) static struct ppb_device * vpoprobe(struct ppb_data *ppb) { - struct vpo_data *vpo; + struct ppb_device *dev; if (nvpo >= MAXVP0) { printf("vpo: Too many devices (max %d)\n", MAXVP0); @@ -155,20 +156,18 @@ vpoprobe(struct ppb_data *ppb) /* vpo dependent initialisation */ vpo->vpo_unit = nvpo; - /* ppbus dependent initialisation */ - vpo->vpo_dev.id_unit = vpo->vpo_unit; - vpo->vpo_dev.ppb = ppb; + /* ok, go to next device on next probe */ + nvpo ++; + + /* low level probe */ + vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); - /* now, try to initialise the drive */ - if (vpo_detect(vpo)) { + if (!(dev = vpoio_probe(ppb, &vpo->vpo_io))) { free(vpo, M_DEVBUF); return (NULL); } - /* ok, go to next device on next probe */ - nvpo ++; - - return (&vpo->vpo_dev); + return (dev); } /* @@ -183,6 +182,10 @@ vpoattach(struct ppb_device *dev) struct scsibus_data *scbus; struct vpo_data *vpo = vpodata[dev->id_unit]; + /* low level attachment */ + if (!vpoio_attach(&vpo->vpo_io)) + return (0); + vpo->sc_link.adapter_unit = vpo->vpo_unit; vpo->sc_link.adapter_targ = VP0_INITIATOR; vpo->sc_link.adapter = &vpo_switch; @@ -190,12 +193,6 @@ vpoattach(struct ppb_device *dev) vpo->sc_link.opennings = VP0_OPENNINGS; /* - * Report ourselves - */ - printf("vpo%d: <Adaptec aic7110 scsi> on ppbus %d\n", - dev->id_unit, dev->ppb->ppb_link->adapter_unit); - - /* * Prepare the scsibus_data area for the upperlevel * scsi code. */ @@ -204,6 +201,9 @@ vpoattach(struct ppb_device *dev) return (0); scbus->adapter_link = &vpo->sc_link; + /* all went ok */ + printf("vpo%d: <Iomega PPA-3/VPI0 SCSI controller>\n", dev->id_unit); + scsi_attachdevs(scbus); return (1); @@ -219,58 +219,11 @@ vpominphys(struct buf *bp) return; } -#ifdef VP0_WARNING -static __inline void -vpo_warning(struct vpo_data *vpo, struct scsi_xfer *xs, int timeout) -{ - - switch (timeout) { - case 0: - case VP0_ESELECT_TIMEOUT: - /* log(LOG_WARNING, - "vpo%d: select timeout\n", vpo->vpo_unit); */ - break; - case VP0_EDISCONNECT: - log(LOG_WARNING, - "vpo%d: can't get printer state\n", vpo->vpo_unit); - break; - case VP0_ECONNECT: - log(LOG_WARNING, - "vpo%d: can't get disk state\n", vpo->vpo_unit); - break; - case VP0_ECMD_TIMEOUT: - log(LOG_WARNING, - "vpo%d: command timeout\n", vpo->vpo_unit); - break; - case VP0_EPPDATA_TIMEOUT: - log(LOG_WARNING, - "vpo%d: EPP data timeout\n", vpo->vpo_unit); - break; - case VP0_ESTATUS_TIMEOUT: - log(LOG_WARNING, - "vpo%d: status timeout\n", vpo->vpo_unit); - break; - case VP0_EDATA_OVERFLOW: - log(LOG_WARNING, - "vpo%d: data overflow\n", vpo->vpo_unit); - break; - case VP0_EINTR: - log(LOG_WARNING, - "vpo%d: ppb request interrupted\n", vpo->vpo_unit); - break; - default: - log(LOG_WARNING, - "vpo%d: timeout = %d\n", vpo->vpo_unit, timeout); - break; - } -} -#endif /* VP0_WARNING */ - /* - * vpointr() + * vpo_intr() */ -static __inline void -vpointr(struct vpo_data *vpo, struct scsi_xfer *xs) +static void +vpo_intr(struct vpo_data *vpo, struct scsi_xfer *xs) { int errno; /* error in errno.h */ @@ -278,9 +231,10 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs) if (xs->datalen && !(xs->flags & SCSI_DATA_IN)) bcopy(xs->data, vpo->vpo_buffer, xs->datalen); - errno = vpoio_do_scsi(vpo, VP0_INITIATOR, + errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, xs->sc_link->target, (char *)xs->cmd, xs->cmdlen, - vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count); + vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count, + &vpo->vpo_error); #ifdef VP0_DEBUG printf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", @@ -298,9 +252,6 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs) /* if a timeout occured, no sense */ if (vpo->vpo_error) { -#ifdef VP0_WARNING - vpo_warning(vpo, xs, vpo->vpo_error); -#endif xs->error = XS_TIMEOUT; goto error; } @@ -318,11 +269,12 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs) vpo->vpo_sense.cmd.length = sizeof(xs->sense); vpo->vpo_sense.cmd.control = 0; - errno = vpoio_do_scsi(vpo, VP0_INITIATOR, + errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, xs->sc_link->target, (char *)&vpo->vpo_sense.cmd, sizeof(vpo->vpo_sense.cmd), (char *)&xs->sense, sizeof(xs->sense), - &vpo->vpo_sense.stat, &vpo->vpo_sense.count); + &vpo->vpo_sense.stat, &vpo->vpo_sense.count, + &vpo->vpo_error); if (errno) /* connection to ppbus interrupted */ @@ -376,490 +328,14 @@ vpo_scsi_cmd(struct scsi_xfer *xs) #endif if (xs->flags & SCSI_NOMASK) { - vpointr(vpodata[xs->sc_link->adapter_unit], xs); + vpo_intr(vpodata[xs->sc_link->adapter_unit], xs); return COMPLETE; } - s = VP0_SPL(); + s = splbio(); - vpointr(vpodata[xs->sc_link->adapter_unit], xs); + vpo_intr(vpodata[xs->sc_link->adapter_unit], xs); splx(s); return SUCCESSFULLY_QUEUED; } - -#define vpoio_d_pulse(vpo,b) { \ - ppb_wdtr(&(vpo)->vpo_dev, b); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ -} - -#define vpoio_c_pulse(vpo,b) { \ - ppb_wdtr(&(vpo)->vpo_dev, b); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ - ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \ -} - -static int -vpoio_disconnect(struct vpo_data *vpo) -{ - - vpoio_d_pulse(vpo, 0); - vpoio_d_pulse(vpo, 0x3c); - vpoio_d_pulse(vpo, 0x20); - vpoio_d_pulse(vpo, 0xf); - - return (ppb_release_bus(&vpo->vpo_dev)); -} - -/* - * how : PPB_WAIT or PPB_DONTWAIT - */ -static int -vpoio_connect(struct vpo_data *vpo, int how) -{ - int error; - - if ((error = ppb_request_bus(&vpo->vpo_dev, how))) - return error; - - vpoio_c_pulse(vpo, 0); - vpoio_c_pulse(vpo, 0x3c); - vpoio_c_pulse(vpo, 0x20); - - if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) { - vpoio_c_pulse(vpo, 0xcf); - } else { - vpoio_c_pulse(vpo, 0x8f); - } - - return (0); -} - -/* - * vpoio_in_disk_mode() - * - * Check if we are in disk mode - */ -static int -vpoio_in_disk_mode(struct vpo_data *vpo) -{ - - /* first, set H_AUTO high */ - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); - - /* when H_AUTO is set low, H_FLT should be high */ - ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); - if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0) - return (0); - - /* when H_AUTO is set high, H_FLT should be low */ - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); - if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0) - return (0); - - return (1); -} - -/* - * vpoio_reset() - * - * SCSI reset signal, the drive must be in disk mode - */ -static void -vpoio_reset (struct vpo_data *vpo) -{ - - /* - * SCSI reset signal. - */ - ppb_wdtr(&vpo->vpo_dev, (1 << 7)); - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE); - DELAY(25); - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); - - return; -} - - -/* - * vpo_detect() - * - * Detect and initialise the VP0 adapter. - */ -static int -vpo_detect(struct vpo_data *vpo) -{ - - vpoio_disconnect(vpo); - vpoio_connect(vpo, PPB_DONTWAIT); - - if (!vpoio_in_disk_mode(vpo)) { - vpoio_disconnect(vpo); - return (VP0_EINITFAILED); - } - - /* send SCSI reset signal */ - vpoio_reset (vpo); - - vpoio_disconnect(vpo); - - if (vpoio_in_disk_mode(vpo)) - return (VP0_EINITFAILED); - - return (0); -} - -#define vpo_wctr(dev,byte,delay) { \ - int i; int iter = delay / MHZ_16_IO_DURATION; \ - for (i = 0; i < iter; i++) { \ - ppb_wctr(dev, byte); \ - } \ -} - -#define vpoio_spp_outbyte(vpo,byte) { \ - ppb_wdtr(&vpo->vpo_dev, byte); \ - ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \ - vpo_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE, \ - VP0_SPP_WRITE_PULSE); \ -} - -#define vpoio_nibble_inbyte(vpo,buffer) { \ - register char h, l; \ - vpo_wctr(&vpo->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE, \ - VP0_NIBBLE_READ_PULSE); \ - h = ppb_rstr(&vpo->vpo_dev); \ - ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \ - l = ppb_rstr(&vpo->vpo_dev); \ - *buffer = ((l >> 4) & 0x0f) + (h & 0xf0); \ -} - -#define vpoio_ps2_inbyte(vpo,buffer) { \ - *buffer = ppb_rdtr(&vpo->vpo_dev); \ - ppb_wctr(&vpo->vpo_dev, PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE); \ - ppb_wctr(&vpo->vpo_dev, PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE); \ -} - -/* - * vpoio_outstr() - */ -static int -vpoio_outstr(struct vpo_data *vpo, char *buffer, int size) -{ - - register int k; - int error = 0; - int r, mode, epp; - - mode = ppb_get_mode(&vpo->vpo_dev); - switch (mode) { - case PPB_NIBBLE: - case PPB_PS2: - for (k = 0; k < size; k++) { - vpoio_spp_outbyte(vpo, *buffer++); - } - break; - - case PPB_EPP: - case PPB_ECP_EPP: - epp = ppb_get_epp_protocol(&vpo->vpo_dev); - - ppb_reset_epp_timeout(&vpo->vpo_dev); - ppb_wctr(&vpo->vpo_dev, - H_AUTO | H_SELIN | H_INIT | H_STROBE); - - if (epp == EPP_1_7) - for (k = 0; k < size; k++) { - ppb_wepp(&vpo->vpo_dev, *buffer++); - if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { - error = VP0_EPPDATA_TIMEOUT; - break; - } - } - else { - if (((long) buffer | size) & 0x03) - ppb_outsb_epp(&vpo->vpo_dev, - buffer, size); - else - ppb_outsl_epp(&vpo->vpo_dev, - buffer, size/4); - - if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { - error = VP0_EPPDATA_TIMEOUT; - break; - } - } - ppb_wctr(&vpo->vpo_dev, - H_AUTO | H_nSELIN | H_INIT | H_STROBE); - /* ppb_ecp_sync(&vpo->vpo_dev); */ - break; - - default: - printf("vpoio_outstr(): unknown transfer mode (%d)!\n", - mode); - return (1); /* XXX */ - } - - return (error); -} - -/* - * vpoio_instr() - */ -static int -vpoio_instr(struct vpo_data *vpo, char *buffer, int size) -{ - - register int k; - int error = 0; - int r, mode, epp; - - mode = ppb_get_mode(&vpo->vpo_dev); - switch (mode) { - case PPB_NIBBLE: - for (k = 0; k < size; k++) { - vpoio_nibble_inbyte(vpo, buffer++); - } - ppb_wctr(&vpo->vpo_dev, - H_AUTO | H_nSELIN | H_INIT | H_STROBE); - break; - - case PPB_PS2: - ppb_wctr(&vpo->vpo_dev, PCD | - H_AUTO | H_SELIN | H_INIT | H_nSTROBE); - - for (k = 0; k < size; k++) { - vpoio_ps2_inbyte(vpo, buffer++); - } - ppb_wctr(&vpo->vpo_dev, - H_AUTO | H_nSELIN | H_INIT | H_STROBE); - break; - - case PPB_EPP: - case PPB_ECP_EPP: - epp = ppb_get_epp_protocol(&vpo->vpo_dev); - - ppb_reset_epp_timeout(&vpo->vpo_dev); - ppb_wctr(&vpo->vpo_dev, PCD | - H_AUTO | H_SELIN | H_INIT | H_STROBE); - - if (epp == EPP_1_7) - for (k = 0; k < size; k++) { - *buffer++ = ppb_repp(&vpo->vpo_dev); - if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { - error = VP0_EPPDATA_TIMEOUT; - break; - } - } - else { - if (((long) buffer | size) & 0x03) - ppb_insb_epp(&vpo->vpo_dev, - buffer, size); - else - ppb_insl_epp(&vpo->vpo_dev, - buffer, size/4); - - if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { - error = VP0_EPPDATA_TIMEOUT; - break; - } - } - ppb_wctr(&vpo->vpo_dev, PCD | - H_AUTO | H_nSELIN | H_INIT | H_STROBE); - /* ppb_ecp_sync(&vpo->vpo_dev); */ - break; - - default: - printf("vpoio_instr(): unknown transfer mode (%d)!\n", - mode); - return (1); /* XXX */ - } - - return (error); -} - -static __inline char -vpoio_select(struct vpo_data *vpo, int initiator, int target) -{ - - register int k; - - ppb_wdtr(&vpo->vpo_dev, (1 << target)); - ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); - ppb_wdtr(&vpo->vpo_dev, (1 << initiator)); - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE); - - k = 0; - while (!(ppb_rstr(&vpo->vpo_dev) & 0x40) && (k++ < VP0_SELTMO)) - barrier(); - - if (k >= VP0_SELTMO) - return (VP0_ESELECT_TIMEOUT); - - return (0); -} - -/* - * vpoio_wait() - * - * H_SELIN must be low. - */ -static __inline char -vpoio_wait(struct vpo_data *vpo, int tmo) -{ - - register int k; - register char r; - -#if 0 /* broken */ - if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR)) - return (0); - - return (ppb_rstr(&vpo->vpo_dev) & 0xf0); -#endif - - k = 0; - while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo)) - barrier(); - - /* - * Return some status information. - * Semantics : 0xc0 = ZIP wants more data - * 0xd0 = ZIP wants to send more data - * 0xe0 = ZIP wants command - * 0xf0 = end of transfer, ZIP is sending status - */ - if (k < tmo) - return (r & 0xf0); - - return (0); /* command timed out */ -} - -static __inline int -vpoio_do_scsi(struct vpo_data *vpo, int host, int target, char *command, - int clen, char *buffer, int blen, int *result, int *count) -{ - - register char r; - char l, h = 0; - int rw, len, error = 0; - register int k; - - /* - * enter disk state, allocate the ppbus - * - * XXX - * Should we allow this call to be interruptible? - * The only way to report the interruption is to return - * EIO do upper SCSI code :^( - */ - if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) - return (error); - - if (!vpoio_in_disk_mode(vpo)) { - vpo->vpo_error = VP0_ECONNECT; goto error; - } - - if ((vpo->vpo_error = vpoio_select(vpo,host,target))) - goto error; - - /* - * Send the command ... - * - * set H_SELIN low for vpoio_wait(). - */ - ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); - -#ifdef VP0_DEBUG - printf("vpo%d: drive selected, now sending the command...\n", - vpo->vpo_unit); -#endif - - for (k = 0; k < clen; k++) { - if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { - vpo->vpo_error = VP0_ECMD_TIMEOUT; - goto error; - } - if (vpoio_outstr(vpo, &command[k], 1)) { - vpo->vpo_error = VP0_EPPDATA_TIMEOUT; - goto error; - } - } - -#ifdef VP0_DEBUG - printf("vpo%d: command sent, now completing the request...\n", - vpo->vpo_unit); -#endif - - /* - * Completion ... - */ - rw = ((command[0] == READ_COMMAND) || (command[0] == READ_BIG) || - (command[0] == WRITE_COMMAND) || (command[0] == WRITE_BIG)); - - *count = 0; - for (;;) { - - if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { - vpo->vpo_error = VP0_ESTATUS_TIMEOUT; goto error; - } - - /* stop when the ZIP wants to send status */ - if (r == (char)0xf0) - break; - - if (*count >= blen) { - vpo->vpo_error = VP0_EDATA_OVERFLOW; - goto error; - } - len = (rw && ((blen - *count) >= VP0_SECTOR_SIZE)) ? - VP0_SECTOR_SIZE : 1; - - /* ZIP wants to send data? */ - if (r == (char)0xc0) - error = vpoio_outstr(vpo, &buffer[*count], len); - else - error = vpoio_instr(vpo, &buffer[*count], len); - - if (error) { - vpo->vpo_error = error; - goto error; - } - - *count += len; - } - - if (vpoio_instr(vpo, &l, 1)) { - vpo->vpo_error = VP0_EOTHER; goto error; - } - - /* check if the ZIP wants to send more status */ - if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) - if (vpoio_instr(vpo, &h, 1)) { - vpo->vpo_error = VP0_EOTHER+2; goto error; - } - - *result = ((int) h << 8) | ((int) l & 0xff); - -error: - /* return to printer state, release the ppbus */ - vpoio_disconnect(vpo); - return (0); -} diff --git a/sys/dev/ppbus/vpoio.c b/sys/dev/ppbus/vpoio.c new file mode 100644 index 0000000..c853846 --- /dev/null +++ b/sys/dev/ppbus/vpoio.c @@ -0,0 +1,771 @@ +/*- + * Copyright (c) 1998 Nicolas Souchu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: vpoio.c,v 1.1.2.4 1998/06/16 23:35:52 son Exp $ + * + */ + +#ifdef KERNEL +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/buf.h> + +#include <machine/clock.h> + +#endif /* KERNEL */ + +#ifdef KERNEL +#include <sys/kernel.h> +#endif /*KERNEL */ + +#include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> +#include <dev/ppbus/vpoio.h> + +/* + * The driver pools the drive. We may add a timeout queue to avoid + * active polling on nACK. I've tried this but it leads to unreliable + * transfers + */ +#define VP0_SELTMO 5000 /* select timeout */ +#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ +#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ + +/* + * Actually, VP0 timings are more accurate (about few 16MHZ cycles), + * but succeeding in respecting such timings leads to architecture + * dependent considerations. + */ +#define VP0_PULSE 1 + +#define VP0_SECTOR_SIZE 512 +#define VP0_BUFFER_SIZE 0x12000 + +#define n(flags) (~(flags) & (flags)) + +/* + * VP0 connections. + */ +#define H_AUTO n(AUTOFEED) +#define H_nAUTO AUTOFEED +#define H_STROBE n(STROBE) +#define H_nSTROBE STROBE +#define H_BSY n(nBUSY) +#define H_nBSY nBUSY +#define H_SEL SELECT +#define H_nSEL n(SELECT) +#define H_ERR ERROR +#define H_nERR n(ERROR) +#define H_ACK nACK +#define H_nACK n(nACK) +#define H_FLT nFAULT +#define H_nFLT n(nFAULT) +#define H_SELIN n(SELECTIN) +#define H_nSELIN SELECTIN +#define H_INIT nINIT +#define H_nINIT n(nINIT) + +/* + * Microcode to execute very fast I/O sequences at the lowest bus level. + */ + +#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,(int)d_pulse) +char d_pulse[] = { + H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, + H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, + H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, + H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, + H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE +}; + +#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,(int)c_pulse) +char c_pulse[] = { + H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, + H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, + H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, + H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, + H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE +}; + +struct ppb_microseq disconnect_microseq[] = { + MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, + MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) +}; + +struct ppb_microseq connect_epp_microseq[] = { + MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, + MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) +}; + +struct ppb_microseq connect_spp_microseq[] = { + MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, + MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) +}; + +/* + * nibble_inbyte_hook() + * + * Formats high and low nibble into a character + */ +static int +nibble_inbyte_hook (void *p, char *ptr) +{ + struct vpo_nibble *s = (struct vpo_nibble *)p; + + /* increment the buffer pointer */ + *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); + + return (0); +} + +/* + * Macro used to initialize each vpoio_data structure during + * low level attachment + */ +#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ + (vpo)->vpo_nibble_inbyte_msq[2].arg[2].p = \ + (void *)&(vpo)->vpo_nibble.h; \ + (vpo)->vpo_nibble_inbyte_msq[4].arg[2].p = \ + (void *)&(vpo)->vpo_nibble.l; \ + (vpo)->vpo_nibble_inbyte_msq[5].arg[0].f = \ + nibble_inbyte_hook; \ + (vpo)->vpo_nibble_inbyte_msq[5].arg[1].p = \ + (void *)&(vpo)->vpo_nibble; \ +} + +/* + * This is the sub-microseqence for MS_GET in NIBBLE mode + * Retrieve the two nibbles and call the C function to generate the character + * and store it in the buffer (see nibble_inbyte_hook()) + */ +struct ppb_microseq nibble_inbyte_submicroseq[] = { + +/* loop: */ + MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), + MS_DELAY(VP0_PULSE), + MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), + MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), + MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), + + /* do a C call to format the received nibbles */ + MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), + MS_DBRA(-6 /* loop */), + + MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(0) +}; + +/* + * This is the sub-microseqence for MS_GET in PS2 mode + */ +struct ppb_microseq ps2_inbyte_submicroseq[] = { + MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), + +/* loop: */ + MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), + MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE), + MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), + MS_DBRA(-3 /* loop */), + + MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(0) +}; + +/* + * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes + */ +struct ppb_microseq spp_outbyte_submicroseq[] = { + +/* loop: */ + MS_RASSERT_P(1, MS_REG_DTR), + MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), + MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_DELAY(VP0_PULSE), + MS_DBRA(-4 /* loop */), + + /* return from the put call */ + MS_RET(0) +}; + +/* EPP 1.7 microsequences, ptr and len set at runtime */ +struct ppb_microseq epp17_outstr_body[] = { + MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE), + +/* loop: */ + MS_RASSERT_P(1, MS_REG_EPP), + MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */ + MS_DBRA(-2 /* loop */), + + MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(0), +/* error: */ + MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(1) +}; + +struct ppb_microseq epp17_instr_body[] = { + MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE), + +/* loop: */ + MS_RFETCH_P(1, MS_REG_EPP, MS_FETCH_ALL), + MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */ + MS_DBRA(-2 /* loop */), + + MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(0), +/* error: */ + MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_RET(1) +}; + +static int +vpoio_disconnect(struct vpoio_data *vpo) +{ + int ret; + + ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); + return (ppb_release_bus(&vpo->vpo_dev)); +} + +/* + * how : PPB_WAIT or PPB_DONTWAIT + */ +static int +vpoio_connect(struct vpoio_data *vpo, int how) +{ + int error; + int ret; + + if ((error = ppb_request_bus(&vpo->vpo_dev, how))) + return error; + + if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) + ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); + else + ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); + + return (0); +} + +/* + * vpoio_in_disk_mode() + * + * Check if we are in disk mode + * + * XXX should be ported to microseq with MS_ASSERT() + */ +static int +vpoio_in_disk_mode(struct vpoio_data *vpo) +{ + + /* first, set H_AUTO high */ + ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); + + /* when H_AUTO is set low, H_FLT should be high */ + ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); + if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0) + return (0); + + /* when H_AUTO is set high, H_FLT should be low */ + ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); + if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0) + return (0); + + return (1); +} + +/* + * vpoio_reset() + * + * SCSI reset signal, the drive must be in disk mode + * + * XXX should be ported to microseq with MS_TRIG() + */ +static void +vpoio_reset (struct vpoio_data *vpo) +{ + + /* + * SCSI reset signal. + */ + ppb_wdtr(&vpo->vpo_dev, (1 << VP0_INITIATOR)); + ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE); + DELAY(25); + ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); + + return; +} + +/* + * vpoio_detect() + * + * Detect and initialise the VP0 adapter. + */ +int +vpoio_detect(struct vpoio_data *vpo) +{ + + vpoio_disconnect(vpo); + vpoio_connect(vpo, PPB_DONTWAIT); + + if (!vpoio_in_disk_mode(vpo)) { + vpoio_disconnect(vpo); + return (VP0_EINITFAILED); + } + + /* send SCSI reset signal */ + vpoio_reset(vpo); + + vpoio_disconnect(vpo); + + if (vpoio_in_disk_mode(vpo)) + return (VP0_EINITFAILED); + + return (0); +} + +/* + * vpoio_outstr() + */ +static int +vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) +{ + + int error = 0; + + ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error); + +#if 0 + /* XXX EPP 1.9 not implemented with microsequences */ + else { + + ppb_reset_epp_timeout(&vpo->vpo_dev); + ppb_wctr(&vpo->vpo_dev, + H_AUTO | H_SELIN | H_INIT | H_STROBE); + + if (((long) buffer | size) & 0x03) + ppb_outsb_epp(&vpo->vpo_dev, + buffer, size); + else + ppb_outsl_epp(&vpo->vpo_dev, + buffer, size/4); + + if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { + error = VP0_EPPDATA_TIMEOUT; + goto error; + } + + ppb_wctr(&vpo->vpo_dev, + H_AUTO | H_nSELIN | H_INIT | H_STROBE); + } + /* ppb_ecp_sync(&vpo->vpo_dev); */ +#endif + + return (error); +} + +/* + * vpoio_instr() + */ +static int +vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) +{ + + register int k; + int error = 0; + int r, mode, epp; + + ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error); + +#if 0 + /* XXX EPP 1.9 not implemented with microsequences */ + else { + + ppb_reset_epp_timeout(&vpo->vpo_dev); + ppb_wctr(&vpo->vpo_dev, PCD | + H_AUTO | H_SELIN | H_INIT | H_STROBE); + + if (((long) buffer | size) & 0x03) + ppb_insb_epp(&vpo->vpo_dev, + buffer, size); + else + ppb_insl_epp(&vpo->vpo_dev, + buffer, size/4); + + if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { + error = VP0_EPPDATA_TIMEOUT; + goto error; + } + + ppb_wctr(&vpo->vpo_dev, PCD | + H_AUTO | H_nSELIN | H_INIT | H_STROBE); + } + /* ppb_ecp_sync(&vpo->vpo_dev); */ +#endif + + return (error); +} + +static char +vpoio_select(struct vpoio_data *vpo, int initiator, int target) +{ + register int k; + int ret; + + struct ppb_microseq select_microseq[] = { + + /* parameter list + */ + #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) + #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) + + /* send the select command to the drive */ + MS_DASS(MS_UNKNOWN), + MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), + MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), + MS_DASS(MS_UNKNOWN), + MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), + + /* now, wait until the drive is ready */ + MS_SET(VP0_SELTMO), +/* loop: */ MS_BRSET(H_ACK, 3 /* ready */), + MS_DBRA(-1 /* loop */), +/* error: */ MS_RET(1), +/* ready: */ MS_RET(0) + }; + + /* initialize the select microsequence */ + ppb_MS_init_msq(select_microseq, 2, + SELECT_TARGET, 1 << target, + SELECT_INITIATOR, 1 << initiator); + + ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret); + + if (ret) + return (VP0_ESELECT_TIMEOUT); + + return (0); +} + +/* + * vpoio_wait() + * + * H_SELIN must be low. + * + * XXX should be ported to microseq + */ +static char +vpoio_wait(struct vpoio_data *vpo, int tmo) +{ + + register int k; + register char r; + +#if 0 /* broken */ + if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR)) + return (0); + + return (ppb_rstr(&vpo->vpo_dev) & 0xf0); +#endif + + /* XXX should be ported to microseq */ + k = 0; + while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo)) + ; + + /* + * Return some status information. + * Semantics : 0xc0 = ZIP wants more data + * 0xd0 = ZIP wants to send more data + * 0xe0 = ZIP wants command + * 0xf0 = end of transfer, ZIP is sending status + */ + if (k < tmo) + return (r & 0xf0); + + return (0); /* command timed out */ +} + +/* + * vpoio_probe() + * + * Low level probe of vpo device + * + */ +struct ppb_device * +vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo) +{ + + /* ppbus dependent initialisation */ + vpo->vpo_dev.id_unit = vpo->vpo_unit; + vpo->vpo_dev.name = "vpo"; + vpo->vpo_dev.ppb = ppb; + + /* now, try to initialise the drive */ + if (vpoio_detect(vpo)) { + return (NULL); + } + + return (&vpo->vpo_dev); +} + +/* + * vpoio_attach() + * + * Low level attachment of vpo device + * + */ +int +vpoio_attach(struct vpoio_data *vpo) +{ + int epp; + + /* + * Report ourselves + */ + printf("vpo%d: <Iomega VPI0 Parallel to SCSI adapter> on ppbus %d\n", + vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit); + + /* + * Initialize microsequence code + */ + vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( + sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); + + if (!vpo->vpo_nibble_inbyte_msq) + return (0); + + bcopy((void *)nibble_inbyte_submicroseq, + (void *)vpo->vpo_nibble_inbyte_msq, + sizeof(nibble_inbyte_submicroseq)); + + INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); + + /* + * Initialize mode dependent in/out microsequences + */ + ppb_request_bus(&vpo->vpo_dev, PPB_WAIT); + + /* enter NIBBLE mode to configure submsq */ + if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) { + + ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); + + ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); + } + + /* enter PS2 mode to configure submsq */ + if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) { + + ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq); + + ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); + } + + epp = ppb_get_epp_protocol(&vpo->vpo_dev); + + /* enter EPP mode to configure submsq */ + if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { + + switch (epp) { + case EPP_1_9: + /* XXX EPP 1.9 support should be improved */ + case EPP_1_7: + ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body); + + ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body); + break; + default: + panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, + epp); + } + } + + /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ + if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { + switch (epp) { + case EPP_1_9: + printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit); + break; + case EPP_1_7: + printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit); + break; + default: + panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, + epp); + } + } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) + printf("vpo%d: PS2 mode\n", vpo->vpo_unit); + + else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) + printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); + + else { + printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n", + vpo->vpo_unit); + + ppb_release_bus(&vpo->vpo_dev); + + free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); + return (0); + } + + ppb_release_bus(&vpo->vpo_dev); + + return (1); +} + +/* + * vpoio_reset_bus() + * + */ +int +vpoio_reset_bus(struct vpoio_data *vpo) +{ + /* first, connect to the drive */ + if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || + (vpoio_in_disk_mode(vpo) == 0)) { + + /* release ppbus */ + vpoio_disconnect(vpo); + return (1); + } + + /* reset the SCSI bus */ + vpoio_reset(vpo); + + /* then disconnect */ + vpoio_disconnect(vpo); + + return (0); +} + +/* + * vpoio_do_scsi() + * + * Send an SCSI command + * + */ +int +vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, + int clen, char *buffer, int blen, int *result, int *count, + int *ret) +{ + + register char r; + char l, h = 0; + int len, error = 0; + register int k; + + /* + * enter disk state, allocate the ppbus + * + * XXX + * Should we allow this call to be interruptible? + * The only way to report the interruption is to return + * EIO do upper SCSI code :^( + */ + if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) + return (error); + + if (!vpoio_in_disk_mode(vpo)) { + *ret = VP0_ECONNECT; goto error; + } + + if ((*ret = vpoio_select(vpo,host,target))) + goto error; + + /* + * Send the command ... + * + * set H_SELIN low for vpoio_wait(). + */ + ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); + + for (k = 0; k < clen; k++) { + if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { + *ret = VP0_ECMD_TIMEOUT; + goto error; + } + if (vpoio_outstr(vpo, &command[k], 1)) { + *ret = VP0_EPPDATA_TIMEOUT; + goto error; + } + } + + /* + * Completion ... + */ + + *count = 0; + for (;;) { + + if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { + *ret = VP0_ESTATUS_TIMEOUT; goto error; + } + + /* stop when the ZIP wants to send status */ + if (r == (char)0xf0) + break; + + if (*count >= blen) { + *ret = VP0_EDATA_OVERFLOW; + goto error; + } + len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? + VP0_SECTOR_SIZE : 1; + + /* ZIP wants to send data? */ + if (r == (char)0xc0) + error = vpoio_outstr(vpo, &buffer[*count], len); + else + error = vpoio_instr(vpo, &buffer[*count], len); + + if (error) { + *ret = error; + goto error; + } + + *count += len; + } + + if (vpoio_instr(vpo, &l, 1)) { + *ret = VP0_EOTHER; goto error; + } + + /* check if the ZIP wants to send more status */ + if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) + if (vpoio_instr(vpo, &h, 1)) { + *ret = VP0_EOTHER+2; goto error; + } + + *result = ((int) h << 8) | ((int) l & 0xff); + +error: + /* return to printer state, release the ppbus */ + vpoio_disconnect(vpo); + return (0); +} diff --git a/sys/dev/ppbus/vpo.h b/sys/dev/ppbus/vpoio.h index 5a724dd..40c5c01 100644 --- a/sys/dev/ppbus/vpo.h +++ b/sys/dev/ppbus/vpoio.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,21 +23,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: vpo.h,v 1.1 1997/08/14 13:57:45 msmith Exp $ + * $Id: vpoio.h,v 1.1.2.3 1998/06/14 15:37:21 son Exp $ * */ -#ifndef __VP03_H -#define __VP03_H - -#define barrier() __asm__("": : :"memory") +#ifndef __VP0IO_H +#define __VP0IO_H +/* + * The ZIP drive cannot act as an initiator. + */ #define VP0_INITIATOR 0x7 -#define VP0_SECTOR_SIZE 512 -#define VP0_BUFFER_SIZE 0x12000 - -#define VP0_SPL() splbio() - #define VP0_ESELECT_TIMEOUT 1 #define VP0_ECMD_TIMEOUT 2 #define VP0_ECONNECT 3 @@ -53,58 +49,36 @@ #define VP0_OPENNINGS 1 -#define n(flags) (~(flags) & (flags)) - /* - * VP0 timings. + * Data structure used during microsequence execution + * when characters are received in nibble mode */ -#define MHZ_16_IO_DURATION 62 +struct vpo_nibble { + char h; /* most significant nibble */ + char l; /* less significant nibble */ +}; -#define VP0_SPP_WRITE_PULSE 253 -#define VP0_NIBBLE_READ_PULSE 486 +struct vpoio_data { + unsigned short int vpo_unit; -/* - * VP0 connections. - */ -#define H_AUTO n(AUTOFEED) -#define H_nAUTO AUTOFEED -#define H_STROBE n(STROBE) -#define H_nSTROBE STROBE -#define H_BSY n(nBUSY) -#define H_nBSY n_BUSY -#define H_SEL SELECT -#define H_nSEL n(SELECT) -#define H_ERR ERROR -#define H_nERR n(ERROR) -#define H_ACK nACK -#define H_nACK n(nACK) -#define H_FLT nFAULT -#define H_nFLT n(nFAULT) -#define H_SELIN n(SELECTIN) -#define H_nSELIN SELECTIN -#define H_INIT nINIT -#define H_nINIT n(nINIT) + struct vpo_nibble vpo_nibble; -struct vpo_sense { - struct scsi_sense cmd; - unsigned int stat; - unsigned int count; -}; + /* each device must have its own nibble inbyte microsequence */ + struct ppb_microseq *vpo_nibble_inbyte_msq; -struct vpo_data { - unsigned short vpo_unit; + struct ppb_device vpo_dev; +}; - int vpo_stat; - int vpo_count; - int vpo_error; +#define vpoio_set_unit(vpo,unit) ((vpo)->vpo_unit = unit) - struct ppb_status vpo_status; - struct vpo_sense vpo_sense; +struct ppb_device *vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo); - unsigned char vpo_buffer[VP0_BUFFER_SIZE]; +int vpoio_attach(struct vpoio_data *vpo); +int vpoio_detect(struct vpoio_data *vpo); +int vpoio_reset_bus(struct vpoio_data *vpo); - struct ppb_device vpo_dev; - struct scsi_link sc_link; -}; +int vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, + int clen, char *buffer, int blen, int *result, int *count, + int *ret); #endif diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c index 39d0ece..71daa85 100644 --- a/sys/dev/ppc/ppc.c +++ b/sys/dev/ppc/ppc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.2 1997/09/01 02:08:41 bde Exp $ + * $Id: ppc.c,v 1.3 1998/04/17 22:36:37 des Exp $ * */ #include "ppc.h" @@ -44,6 +44,8 @@ #include <i386/isa/isa_device.h> #include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> + #include <i386/isa/ppcreg.h> static int ppcprobe(struct isa_device *); @@ -57,14 +59,26 @@ static struct ppc_data *ppcdata[NPPC]; static int nppc = 0; static char *ppc_types[] = { - "SMC", "SMC FDC37C665GT", "SMC FDC37C666GT", - "NatSemi", "PC87332", "PC87306", - "Intel 82091AA", "Generic", 0 + "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", + "82091AA", "Generic", "W83877F", "W83877AF", "Winbond", 0 +}; + +/* list of available modes */ +static char *ppc_avms[] = { + "COMPATIBLE", "NIBBLE-only", "PS2-only", "PS2/NIBBLE", "EPP-only", + "EPP/NIBBLE", "EPP/PS2", "EPP/PS2/NIBBLE", "ECP-only", + "ECP/NIBBLE", "ECP/PS2", "ECP/PS2/NIBBLE", "ECP/EPP", + "ECP/EPP/NIBBLE", "ECP/EPP/PS2", "ECP/EPP/PS2/NIBBLE", 0 }; +/* list of current executing modes + * Note that few modes do not actually exist. + */ static char *ppc_modes[] = { - "AUTODETECT", "NIBBLE", "PS/2", "EPP", "ECP+EPP", "ECP+PS/2", "ECP", - "UNKNOWN", 0 + "COMPATIBLE", "NIBBLE", "PS/2", "PS/2", "EPP", + "EPP", "EPP", "EPP", "ECP", + "ECP", "ECP+PS2", "ECP+PS2", "ECP+EPP", + "ECP+EPP", "ECP+EPP", "ECP+EPP", 0 }; static char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 }; @@ -110,12 +124,19 @@ static void ppc_wfifo(int unit, char byte) { w_fifo(ppcdata[unit], byte); } static void ppc_reset_epp_timeout(int); static void ppc_ecp_sync(int); +static int ppc_exec_microseq(int, struct ppb_microseq *, int *); +static int ppc_generic_setmode(int, int); + static struct ppb_adapter ppc_adapter = { 0, /* no intr handler, filled by chipset dependent code */ ppc_reset_epp_timeout, ppc_ecp_sync, + ppc_exec_microseq, + + ppc_generic_setmode, + ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -143,8 +164,8 @@ ppc_ecp_sync(int unit) { DELAY(100); } - printf("ppc: ECP sync failed as data still " \ - "present in FIFO.\n"); + printf("ppc%d: ECP sync failed as data still " \ + "present in FIFO.\n", unit); return; } @@ -158,6 +179,35 @@ ppcintr(int unit) return; } +static void +ppc_ecp_config(struct ppc_data *ppc, int chipset_mode) +{ + /* XXX disable DMA, enable interrupts */ + if (chipset_mode & PPB_EPP) + /* select EPP mode */ + w_ecr(ppc, 0x80); + else if (chipset_mode & PPB_PS2) + /* select PS2 mode with ECP */ + w_ecr(ppc, 0x20); + else + /* keep ECP mode alone, default for NIBBLE */ + w_ecr(ppc, 0x70); + + return; +} + +static int +ppc_detect_port(struct ppc_data *ppc) +{ + + w_ctr(ppc, 0x0c); /* To avoid missing PS2 ports */ + w_dtr(ppc, 0xaa); + if (r_dtr(ppc) != (char) 0xaa) + return (0); + + return (1); +} + /* * ppc_pc873xx_detect * @@ -170,11 +220,11 @@ static int pc873xx_basetab[] = {0x0398, 0x026e, 0x015c, 0x002e, 0}; static int pc873xx_porttab[] = {0x0378, 0x03bc, 0x0278, 0}; static int -ppc_pc873xx_detect(struct ppc_data *ppc) +ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never forced */ { static int index = 0; int base, idport; - int val, mode; + int val; while ((idport = pc873xx_basetab[index++])) { @@ -235,7 +285,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) printf("PC873xx locked\n"); /* work out what mode we're in */ - mode = PPB_NIBBLE; /* worst case */ + ppc->ppc_avm |= PPB_NIBBLE; /* worst case */ outb(idport, PC873_PCR); val = inb(idport + 1); @@ -243,10 +293,10 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport, PC873_PTR); val = inb(idport + 1); if (!(val & PC873_EPPRDIR)) { - mode = PPB_EPP; /* As we would have done it anwyay */ + ppc->ppc_avm |= PPB_EPP; /* As we would have done it anwyay */ } } else if ((val & PC873_ECPEN) && (val & PC873_ECPCLK)) { - mode = PPB_PS2; /* tolerable alternative */ + ppc->ppc_avm |= PPB_PS2; /* tolerable alternative */ } } else { if (bootverbose) @@ -299,7 +349,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, val); /* we are an EPP-32 port */ - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP; } else { if (bootverbose) printf("ECP\n"); @@ -309,44 +359,21 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, inb(idport + 1) | PC873_ECPEN | PC873_ECPCLK); /* we look like a PS/2 port */ - mode = PPB_PS2; + ppc->ppc_avm |= PPB_PS2; } } - return(mode); + + return(chipset_mode); } - return(0); + return(-1); } static int -ppc_detect_ps2(struct ppc_data *ppc) +ppc_check_epp_timeout(struct ppc_data *ppc) { - char save_control, r; - - save_control = r_ctr(ppc); - - /* Try PS/2 mode */ - w_ctr(ppc, 0xec); - w_dtr(ppc, 0x55); - - /* needed if in ECP mode */ - if (ppc->ppc_mode == PPB_ECP) - w_ctr(ppc, PCD | 0xec); - r = r_dtr(ppc); - - if (r != (char) 0xff) { - if (r != (char) 0x55) - return 0; - - w_dtr(ppc, 0xaa); - r = r_dtr(ppc); - if (r != (char) 0xaa) - return 0; - - return (PPB_NIBBLE); - } else - w_ctr(ppc, save_control); + ppc_reset_epp_timeout(ppc->ppc_unit); - return (PPB_PS2); + return (!(r_str(ppc) & TIMEOUT)); } /* @@ -355,11 +382,10 @@ ppc_detect_ps2(struct ppc_data *ppc) * SMC FDC37C66xGT configuration. */ static int -ppc_smc37c66xgt_detect(struct ppc_data *ppc, int mode) +ppc_smc37c66xgt_detect(struct ppc_data *ppc, int chipset_mode) { int s, i; char r; - int retry = 0; /* boolean */ int type = -1; int csr = SMC66x_CSR; /* initial value is 0x3F0 */ @@ -404,7 +430,7 @@ config: * If chipset not found, do not continue. */ if (type == -1) - return (0); + return (-1); /* select CR1 */ outb(csr, 0x1); @@ -412,65 +438,72 @@ config: /* read the port's address: bits 0 and 1 of CR1 */ r = inb(cio) & SMC_CR1_ADDR; if (port_address[r] != ppc->ppc_base) - return (0); + return (-1); ppc->ppc_type = type; /* * CR1 and CR4 registers bits 3 and 0/1 for mode configuration - * If SPP mode is detected, try to set ECP+EPP mode end retry - * detection to verify. + * If SPP mode is detected, try to set ECP+EPP mode */ -retry: - /* select CR1 register */ + if (bootverbose) { + outb(csr, 0x1); + printf("SMC registers CR1=0x%x", ppc->ppc_unit, + inb(cio) & 0xff); + + outb(csr, 0x4); + printf(" CR4=0x%x", inb(cio) & 0xff); + } + + /* select CR1 */ outb(csr, 0x1); - if (!mode) { + if (!chipset_mode) { /* autodetect mode */ - /* 666GT chipset is hardwired to an extended mode */ - if (type == SMC_37C666GT) - mode = PPB_ECP_EPP; + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ + if (type == SMC_37C666GT) { + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; - else if ((inb(cio) & SMC_CR1_MODE) == 0) { + } else + if ((inb(cio) & SMC_CR1_MODE) == 0) { /* already in extended parallel port mode, read CR4 */ outb(csr, 0x4); r = (inb(cio) & SMC_CR4_EMODE); switch (r) { case SMC_SPP: - /* let's detect NIBBLE or PS/2 later */ + ppc->ppc_avm |= PPB_SPP; break; case SMC_EPPSPP: - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP | PPB_SPP; break; case SMC_ECP: - /* - * Yet, don't know what to do with it! XXX - * So, consider ECP mode as PS/2. - * (see configuration later). - */ - mode = PPB_ECP; + ppc->ppc_avm |= PPB_ECP | PPB_SPP; break; case SMC_ECPEPP: - mode = PPB_ECP_EPP; + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; break; } - } + } else { + /* not an extended port mode */ + ppc->ppc_avm |= PPB_SPP; + } + } else { /* mode forced */ - /* 666GT chipset is hardwired to an extended mode */ + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ if (type == SMC_37C666GT) goto end_detect; r = inb(cio); - if (mode == PPB_NIBBLE || mode == PPB_PS2) { - /* do not use ECP when the mode is forced to SPP */ + if ((chipset_mode & (PPB_ECP | PPB_EPP)) == 0) { + /* do not use ECP when the mode is not forced to */ outb(cio, r | SMC_CR1_MODE); } else { /* an extended mode is selected */ @@ -480,30 +513,26 @@ retry: outb(csr, 0x4); r = inb(cio) & ~SMC_CR4_EMODE; - switch (mode) { - case PPB_EPP: + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) { + outb(cio, r | SMC_ECPEPP); + } else { + outb(cio, r | SMC_ECP); + } + } else { + /* PPB_EPP is set */ outb(cio, r | SMC_EPPSPP); - break; - - case PPB_ECP: - case PPB_ECP_PS2: - outb(cio, r | SMC_ECP); - break; - - case PPB_ECP_EPP: - outb(cio, r | SMC_ECPEPP); - break; - - default: - printf("ppc: unknown mode (%d)\n", - mode); - return (0); } } + ppc->ppc_avm = chipset_mode; } end_detect: - if (PPB_IS_EPP(mode)) { + + if (bootverbose) + printf ("\n"); + + if (chipset_mode & PPB_EPP) { /* select CR4 */ outb(csr, 0x4); r = inb(cio); @@ -511,11 +540,9 @@ end_detect: /* * Set the EPP protocol... * Low=EPP 1.9 (1284 standard) and High=EPP 1.7 - * ...then check the result. */ if (ppc->ppc_epp == EPP_1_9) outb(cio, (r & ~SMC_CR4_EPPTYPE)); - else outb(cio, (r | SMC_CR4_EPPTYPE)); } @@ -523,86 +550,214 @@ end_detect: /* end config mode */ outb(csr, 0xaa); - /* - * Write 100 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP_EPP) - w_ecr(ppc, 0x80); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - /* - * Write 001 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP) - w_ecr(ppc, 0x20); + return (chipset_mode); +} + +/* + * Winbond W83877F stuff + * + * EFER: extended function enable register + * EFIR: extended function index register + * EFDR: extended function data register + */ +#define efir ((efer == 0x250) ? 0x251 : 0x3f0) +#define efdr ((efer == 0x250) ? 0x252 : 0x3f1) + +static int w83877f_efers[] = { 0x250, 0x3f0, 0x3f0, 0x250 }; +static int w83877f_keys[] = { 0x89, 0x86, 0x87, 0x88 }; +static int w83877f_keyiter[] = { 1, 2, 2, 1 }; +static int w83877f_hefs[] = { WINB_HEFERE, WINB_HEFRAS, WINB_HEFERE | WINB_HEFRAS, 0 }; + +static int +ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode) +{ + int i, j, efer, base; + unsigned char r, hefere, hefras; + + for (i = 0; i < 4; i ++) { + /* first try to enable configuration registers */ + efer = w83877f_efers[i]; + + /* write the key to the EFER */ + for (j = 0; j < w83877f_keyiter[i]; j ++) + outb (efer, w83877f_keys[i]); + + /* then check HEFERE and HEFRAS bits */ + outb (efir, 0x0c); + hefere = inb(efdr) & WINB_HEFERE; + + outb (efir, 0x16); + hefras = inb(efdr) & WINB_HEFRAS; - if (PPB_IS_EPP(mode)) { /* - * Try to reset EPP timeout bit. - * If it fails, try PS/2 and NIBBLE modes. + * HEFRAS HEFERE + * 0 1 write 89h to 250h (power-on default) + * 1 0 write 86h twice to 3f0h + * 1 1 write 87h twice to 3f0h + * 0 0 write 88h to 250h */ - ppc_reset_epp_timeout(ppc->ppc_unit); + if ((hefere | hefras) == w83877f_hefs[i]) + goto found; + } - r = r_str(ppc); - if (!(r & TIMEOUT)) - return (mode); - } else { - if (mode) - return (mode); + return (-1); /* failed */ + +found: + /* check base port address - read from CR23 */ + outb(efir, 0x23); + if (ppc->ppc_base != inb(efdr) * 4) /* 4 bytes boundaries */ + return (-1); + + /* read CHIP ID from CR9/bits0-3 */ + outb(efir, 0x9); + + switch (inb(efdr) & WINB_CHIPID) { + case WINB_W83877F_ID: + ppc->ppc_type = WINB_W83877F; + break; + + case WINB_W83877AF_ID: + ppc->ppc_type = WINB_W83877AF; + break; + + default: + ppc->ppc_type = WINB_UNKNOWN; } - /* detect PS/2 or NIBBLE mode */ - return (ppc_detect_ps2(ppc)); -} + if (bootverbose) { + /* dump of registers */ + printf("ppc%d: 0x%x - ", ppc->ppc_unit, w83877f_keys[i]); + for (i = 0; i <= 0xd; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + for (i = 0x10; i <= 0x17; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + outb(efir, 0x1e); + printf("0x%x ", inb(efdr)); + for (i = 0x20; i <= 0x29; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + printf("\n"); + } -static int -ppc_check_ecpepp_timeout(struct ppc_data *ppc) -{ - char r; + if (!chipset_mode) { + /* autodetect mode */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* select CR0 */ + outb(efir, 0x0); + r = inb(efdr) & (WINB_PRTMODS0 | WINB_PRTMODS1); + + /* select CR9 */ + outb(efir, 0x9); + r |= (inb(efdr) & WINB_PRTMODS2); + + switch (r) { + case WINB_W83757: + if (bootverbose) + printf("ppc%d: W83757 compatible mode\n", + ppc->ppc_unit); + return (-1); /* generic or SMC-like */ + + case WINB_EXTFDC: + case WINB_EXTADP: + case WINB_EXT2FDD: + case WINB_JOYSTICK: + if (bootverbose) + printf("ppc%d: not in parallel port mode\n", + ppc->ppc_unit); + return (-1); + + case (WINB_PARALLEL | WINB_EPP_SPP): + ppc->ppc_avm |= PPB_EPP | PPB_SPP; + break; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_ECP_EPP); + case (WINB_PARALLEL | WINB_ECP): + ppc->ppc_avm |= PPB_ECP | PPB_SPP; + break; + + case (WINB_PARALLEL | WINB_ECP_EPP): + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; + break; + default: + printf("%s: unknown case (0x%x)!\n", __FUNCTION__, r); + } + + } else { + /* mode forced */ + + /* select CR9 and set PRTMODS2 bit */ + outb(efir, 0x9); + outb(efdr, inb(efdr) & ~WINB_PRTMODS2); + + /* select CR0 and reset PRTMODSx bits */ + outb(efir, 0x0); + outb(efdr, inb(efdr) & ~(WINB_PRTMODS0 | WINB_PRTMODS1)); + + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) + outb(efdr, inb(efdr) | WINB_ECP_EPP); + else + outb(efdr, inb(efdr) | WINB_ECP); + } else { + /* select EPP_SPP otherwise */ + outb(efdr, inb(efdr) | WINB_EPP_SPP); + } + ppc->ppc_avm = chipset_mode; } + + /* exit configuration mode */ + outb(efer, 0xaa); - /* If EPP timeout bit is not reset, DON'T use EPP */ - w_ecr(ppc, 0x20); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (PPB_ECP_PS2); + return (chipset_mode); } /* * ppc_generic_detect */ static int -ppc_generic_detect(struct ppc_data *ppc, int mode) +ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) { - char save_control, r; + char save_control; + + if (!chipset_mode) { + /* first, check for ECP */ + w_ecr(ppc, 0x20); + if ((r_ecr(ppc) & 0xe0) == 0x20) { + ppc->ppc_avm |= PPB_ECP | PPB_SPP; - /* don't know what to do here */ - if (mode) - return (mode); + /* search for SMC style ECP+EPP mode */ + w_ecr(ppc, 0x80); + } - /* try to reset EPP timeout bit */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* try to reset EPP timeout bit */ + if (ppc_check_epp_timeout(ppc)) { + ppc->ppc_avm |= PPB_EPP; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_EPP); - } + if (ppc->ppc_avm & PPB_ECP) + /* SMC like chipset found */ + ppc->ppc_type = SMC_LIKE; + } - /* Now check for ECP */ - w_ecr(ppc, 0x20); - r = r_ecr(ppc); - if ((r & 0xe0) == 0x20) { - /* Search for SMC style EPP+ECP mode */ - w_ecr(ppc, 0x80); + /* XXX try to detect NIBBLE mode */ + ppc->ppc_avm |= PPB_NIBBLE; - return (ppc_check_ecpepp_timeout(ppc)); - } + } else + ppc->ppc_avm = chipset_mode; + + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (ppc_detect_ps2(ppc)); + return (chipset_mode); } /* @@ -611,22 +766,258 @@ ppc_generic_detect(struct ppc_data *ppc, int mode) * mode is the mode suggested at boot */ static int -ppc_detect(struct ppc_data *ppc, int mode) { +ppc_detect(struct ppc_data *ppc, int chipset_mode) { - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_pc873xx_detect(ppc))) - goto end_detect; + int i, mode; - if (!ppc->ppc_mode && (ppc->ppc_mode = - ppc_smc37c66xgt_detect(ppc, mode))) - goto end_detect; + /* list of supported chipsets */ + int (*chipset_detect[])(struct ppc_data *, int) = { + ppc_pc873xx_detect, + ppc_smc37c66xgt_detect, + ppc_w83877f_detect, + ppc_generic_detect, + NULL + }; - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_generic_detect(ppc, mode))) - goto end_detect; + /* if can't find the port and mode not forced return error */ + if (!ppc_detect_port(ppc) && chipset_mode == 0) + return (EIO); /* failed, port not present */ - printf("ppc: port not present at 0x%x.\n", ppc->ppc_base); - return (PPC_ENOPORT); + /* assume centronics compatible mode is supported */ + ppc->ppc_avm = PPB_COMPATIBLE; -end_detect: + /* we have to differenciate available chipset modes, + * chipset running modes and IEEE-1284 operating modes + * + * after detection, the port must support running in compatible mode + */ + for (i=0; chipset_detect[i] != NULL; i++) { + if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) { + ppc->ppc_mode = mode; + break; + } + } + + return (0); +} + +/* + * ppc_exec_microseq() + * + * Execute a microsequence. + * Microsequence mechanism is supposed to handle fast I/O operations. + */ +static int +ppc_exec_microseq(int unit, struct ppb_microseq *msq, int *ppbpc) +{ + struct ppc_data *ppc = ppcdata[unit]; + struct ppb_microseq *pc; + char cc, *p; + int i, iter, reg; + int error; + + /* static to be reused after few ppc_exec_microseq()/return calls + * XXX should be in a context variable shared with ppb level */ + static int accum; + static char *ptr; + + struct ppb_microseq *microseq_stack = 0; + struct ppb_microseq *pc_stack = 0; + +/* microsequence registers are equivalent to PC-like port registers */ +#define r_reg(register,ppc) ((char)inb((ppc)->ppc_base + register)) +#define w_reg(register,ppc,byte) outb((ppc)->ppc_base + register, byte) + +#define INCR_PC (pc ++) /* increment program counter */ +#define mi pc /* microinstruction currently executed */ + + /* get the state of pc from ppb level of execution */ + pc = &msq[*ppbpc]; + + for (;;) { + + switch (mi->opcode) { + case MS_OP_RSET: + cc = r_reg(mi->arg[0].i, ppc); + cc &= mi->arg[2].c; /* clear mask */ + cc |= mi->arg[1].c; /* assert mask */ + w_reg(mi->arg[0].i, ppc, cc); + INCR_PC; + break; + + case MS_OP_RASSERT_P: + for (i=0; i<mi->arg[0].i; i++) + w_reg(mi->arg[1].i, ppc, *ptr++); + INCR_PC; + break; + + case MS_OP_RFETCH_P: + for (i=0; i<mi->arg[0].i; i++) + *ptr++ = r_reg(mi->arg[1].i, ppc) & + mi->arg[2].c; + INCR_PC; + break; + + case MS_OP_RFETCH: + *((char *) mi->arg[2].p) = r_reg(mi->arg[0].i, ppc) & + mi->arg[1].c; + INCR_PC; + break; + + case MS_OP_RASSERT: + + /* let's suppose the next instr. is the same */ + prefetch: + for (;mi->opcode == MS_OP_RASSERT; INCR_PC) + w_reg(mi->arg[0].i, ppc, mi->arg[1].c); + + if (mi->opcode == MS_OP_DELAY) { + DELAY(mi->arg[0].i); + INCR_PC; + goto prefetch; + } + break; + + case MS_OP_DELAY: + DELAY(mi->arg[0].i); + INCR_PC; + break; + + case MS_OP_TRIG: + reg = mi->arg[0].i; + iter = mi->arg[1].i; + p = (char *)mi->arg[2].p; + + for (i=0; i<iter; i++) { + w_reg(reg, ppc, *p++); + DELAY((unsigned char)*p++); + } + INCR_PC; + break; + + case MS_OP_SET: + accum = mi->arg[0].i; + INCR_PC; + break; + + case MS_OP_DBRA: + if (--accum > 0) + pc += mi->arg[0].i; + else + INCR_PC; + break; + + case MS_OP_BRSET: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == mi->arg[0].c) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_BRCLEAR: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == 0) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_C_CALL: + /* + * If the C call returns !0 then end the microseq. + * The current state of ptr is passed to the C function + */ + if ((error = mi->arg[0].f(mi->arg[1].p, ptr))) + return (error); + + INCR_PC; + break; + + case MS_OP_PTR: + ptr = (char *)mi->arg[0].p; + INCR_PC; + break; + + case MS_OP_CALL: + if (microseq_stack) + panic("%s: too much calls", __FUNCTION__); + + if (mi->arg[0].p) { + /* store the state of the actual + * microsequence + */ + microseq_stack = msq; + pc_stack = pc; + + /* jump to the new microsequence */ + msq = (struct ppb_microseq *)mi->arg[0].p; + pc = msq; + } else + INCR_PC; + + break; + + case MS_OP_SUBRET: + /* retrieve microseq and pc state before the call */ + msq = microseq_stack; + pc = pc_stack; + + /* reset the stack */ + microseq_stack = 0; + + /* XXX return code */ + + INCR_PC; + break; + + case MS_OP_PUT: + case MS_OP_GET: + case MS_OP_RET: + /* can't return to ppb level during the execution + * of a submicrosequence */ + if (microseq_stack) + panic("%s: can't return to ppb level", + __FUNCTION__); + + /* update pc for ppb level of execution */ + *ppbpc = (int)(pc - msq); + + /* return to ppb level of execution */ + return (0); + + default: + panic("%s: unknown microsequence opcode 0x%x", + __FUNCTION__, mi->opcode); + } + } + + /* unreached */ +} + +/* + * Configure current operating mode + */ +static int +ppc_generic_setmode(int unit, int mode) +{ + struct ppc_data *ppc = ppcdata[unit]; + + /* back to compatible mode, XXX don't know yet what to do here */ + if (mode == 0) { + ppc->ppc_mode = PPB_COMPATIBLE; + return (0); + } + + /* check if mode is available */ + if (!(ppc->ppc_avm & mode)) + return (EOPNOTSUPP); + + /* if ECP mode, configure ecr register */ + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, mode); + + ppc->ppc_mode = mode; return (0); } @@ -688,16 +1079,14 @@ ppcprobe(struct isa_device *dvp) ppc->ppc_unit = dvp->id_unit; ppc->ppc_type = GENERIC; - /* PPB_AUTODETECT is default to allow chipset detection even if - * mode is forced by dvp->id_flags (see later, ppc_detect() call) */ - ppc->ppc_mode = PPB_AUTODETECT; - ppc->ppc_epp = (dvp->id_flags & 0x8) >> 3; + ppc->ppc_mode = PPB_COMPATIBLE; + ppc->ppc_epp = (dvp->id_flags & 0x10) >> 4; /* * XXX * Try and detect if interrupts are working. */ - if (!(dvp->id_flags & 0x10)) + if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = (dvp->id_irq); ppcdata[ppc->ppc_unit] = ppc; @@ -706,7 +1095,7 @@ ppcprobe(struct isa_device *dvp) /* * Try to detect the chipset and its mode. */ - if (ppc_detect(ppc, dvp->id_flags & 0x7)) + if (ppc_detect(ppc, dvp->id_flags & 0xf)) goto error; end_probe: @@ -722,6 +1111,7 @@ ppcattach(struct isa_device *isdp) { struct ppc_data *ppc = ppcdata[isdp->id_unit]; struct ppb_data *ppbus; + char * mode; /* * Link the Parallel Port Chipset (adapter) to @@ -730,9 +1120,9 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.adapter_unit = ppc->ppc_unit; ppc->ppc_link.adapter = &ppc_adapter; - printf("ppc%d: %s chipset in %s mode%s\n", ppc->ppc_unit, - ppc_types[ppc->ppc_type], ppc_modes[ppc->ppc_mode], - (PPB_IS_EPP(ppc->ppc_mode)) ? + printf("ppc%d: %s chipset (%s) in %s mode%s\n", ppc->ppc_unit, + ppc_types[ppc->ppc_type], ppc_avms[ppc->ppc_avm], + ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); /* diff --git a/sys/dev/ppc/ppcreg.h b/sys/dev/ppc/ppcreg.h index 7ab6189e..27ab1a1 100644 --- a/sys/dev/ppc/ppcreg.h +++ b/sys/dev/ppc/ppcreg.h @@ -23,23 +23,25 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.1 1997/08/14 14:01:36 msmith Exp $ + * $Id: ppcreg.h,v 1.2 1997/08/16 14:07:26 msmith Exp $ * */ -#ifndef __PPC_H -#define __PPC_H +#ifndef __PPCREG_H +#define __PPCREG_H /* * Parallel Port Chipset type. */ -#define SMC_UNKNOWN 0x0 +#define SMC_LIKE 0x0 #define SMC_37C665GT 0x1 #define SMC_37C666GT 0x2 -#define NS_UNKNOWN 0x3 -#define NS_PC87332 0x4 -#define NS_PC87306 0x5 -#define INTEL_820191AA 0x6 -#define GENERIC 0x7 +#define NS_PC87332 0x3 +#define NS_PC87306 0x4 +#define INTEL_820191AA 0x5 /* XXX not implemented */ +#define GENERIC 0x6 +#define WINB_W83877F 0x7 +#define WINB_W83877AF 0x8 +#define WINB_UNKNOWN 0x9 /* * Generic structure to hold parallel port chipset info. @@ -49,10 +51,13 @@ struct ppc_data { int ppc_unit; int ppc_type; + int ppc_mode; /* chipset current mode */ + int ppc_avm; /* chipset available modes */ + #define ppc_base ppc_link.base -#define ppc_mode ppc_link.mode #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq +#define ppc_subm ppc_link.submicroseq unsigned char ppc_flags; @@ -60,11 +65,6 @@ struct ppc_data { }; /* - * Parallel Port Chipset errors. XXX - */ -#define PPC_ENOPORT 9 - -/* * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ @@ -74,12 +74,12 @@ struct ppc_data { #define PPC_ECP_FIFO 0x400 /* ECP fifo register */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ -#define r_dtr(ppc) inb((ppc)->ppc_base + PPC_SPP_DTR) -#define r_str(ppc) inb((ppc)->ppc_base + PPC_SPP_STR) -#define r_ctr(ppc) inb((ppc)->ppc_base + PPC_SPP_CTR) -#define r_epp(ppc) inb((ppc)->ppc_base + PPC_EPP_DATA) -#define r_ecr(ppc) inb((ppc)->ppc_base + PPC_ECP_ECR) -#define r_fifo(ppc) inb((ppc)->ppc_base + PPC_ECP_FIFO) +#define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) +#define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) +#define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) +#define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) @@ -111,7 +111,7 @@ struct ppc_data { #define PC873_SID 0x08 /* - * Register defines for the SMC FDC37C66xGT parts. + * Register defines for the SMC FDC37C66xGT parts */ /* Init codes */ @@ -124,9 +124,9 @@ struct ppc_data { /* Bits */ #define SMC_CR1_ADDR 0x3 /* bit 0 and 1 */ -#define SMC_CR1_MODE 0x8 /* bit 3 */ +#define SMC_CR1_MODE (1<<3) /* bit 3 */ #define SMC_CR4_EMODE 0x3 /* bits 0 and 1 */ -#define SMC_CR4_EPPTYPE 0x40 /* bit 6 */ +#define SMC_CR4_EPPTYPE (1<<6) /* bit 6 */ /* Extended modes */ #define SMC_SPP 0x0 /* SPP */ @@ -134,5 +134,34 @@ struct ppc_data { #define SMC_ECP 0x2 /* ECP */ #define SMC_ECPEPP 0x3 /* ECP and EPP */ -#endif +/* + * Register defines for the Winbond W83877F parts + */ +#define WINB_W83877F_ID 0xa +#define WINB_W83877AF_ID 0xb + +/* Configuration bits */ +#define WINB_HEFERE (1<<5) /* CROC bit 5 */ +#define WINB_HEFRAS (1<<0) /* CR16 bit 0 */ + +#define WINB_PNPCVS (1<<2) /* CR16 bit 2 */ +#define WINB_CHIPID 0xf /* CR9 bits 0-3 */ + +#define WINB_PRTMODS0 (1<<2) /* CR0 bit 2 */ +#define WINB_PRTMODS1 (1<<3) /* CR0 bit 3 */ +#define WINB_PRTMODS2 (1<<7) /* CR9 bit 7 */ + +/* W83877F modes: CR9/bit7 | CR0/bit3 | CR0/bit2 */ +#define WINB_W83757 0x0 +#define WINB_EXTFDC 0x4 +#define WINB_EXTADP 0x8 +#define WINB_EXT2FDD 0xc +#define WINB_JOYSTICK 0x80 + +#define WINB_PARALLEL 0x80 +#define WINB_EPP_SPP 0x4 +#define WINB_ECP 0x8 +#define WINB_ECP_EPP 0xc + +#endif diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 0ad0237..ab64a7a 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.446 1998/07/11 04:46:27 julian Exp $ +# $Id: LINT,v 1.447 1998/07/20 20:00:30 msmith Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1440,6 +1440,7 @@ options POWERFAIL_NMI # make it beep instead of panicing # Requires SCSI disk support ('scbus' and 'sd'), best # performance is achieved with ports in EPP 1.9 mode. # nlpt Parallel Printer +# plip Parallel network interface # ppi General-purpose I/O ("Geek Port") # # Supported interfaces: @@ -1448,6 +1449,7 @@ options POWERFAIL_NMI # make it beep instead of panicing controller ppbus0 controller vpo0 at ppbus? device nlpt0 at ppbus? +device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 0ad0237..ab64a7a 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.446 1998/07/11 04:46:27 julian Exp $ +# $Id: LINT,v 1.447 1998/07/20 20:00:30 msmith Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1440,6 +1440,7 @@ options POWERFAIL_NMI # make it beep instead of panicing # Requires SCSI disk support ('scbus' and 'sd'), best # performance is achieved with ports in EPP 1.9 mode. # nlpt Parallel Printer +# plip Parallel network interface # ppi General-purpose I/O ("Geek Port") # # Supported interfaces: @@ -1448,6 +1449,7 @@ options POWERFAIL_NMI # make it beep instead of panicing controller ppbus0 controller vpo0 at ppbus? device nlpt0 at ppbus? +device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? diff --git a/sys/i386/isa/ppc.c b/sys/i386/isa/ppc.c index 39d0ece..71daa85 100644 --- a/sys/i386/isa/ppc.c +++ b/sys/i386/isa/ppc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.2 1997/09/01 02:08:41 bde Exp $ + * $Id: ppc.c,v 1.3 1998/04/17 22:36:37 des Exp $ * */ #include "ppc.h" @@ -44,6 +44,8 @@ #include <i386/isa/isa_device.h> #include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> + #include <i386/isa/ppcreg.h> static int ppcprobe(struct isa_device *); @@ -57,14 +59,26 @@ static struct ppc_data *ppcdata[NPPC]; static int nppc = 0; static char *ppc_types[] = { - "SMC", "SMC FDC37C665GT", "SMC FDC37C666GT", - "NatSemi", "PC87332", "PC87306", - "Intel 82091AA", "Generic", 0 + "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", + "82091AA", "Generic", "W83877F", "W83877AF", "Winbond", 0 +}; + +/* list of available modes */ +static char *ppc_avms[] = { + "COMPATIBLE", "NIBBLE-only", "PS2-only", "PS2/NIBBLE", "EPP-only", + "EPP/NIBBLE", "EPP/PS2", "EPP/PS2/NIBBLE", "ECP-only", + "ECP/NIBBLE", "ECP/PS2", "ECP/PS2/NIBBLE", "ECP/EPP", + "ECP/EPP/NIBBLE", "ECP/EPP/PS2", "ECP/EPP/PS2/NIBBLE", 0 }; +/* list of current executing modes + * Note that few modes do not actually exist. + */ static char *ppc_modes[] = { - "AUTODETECT", "NIBBLE", "PS/2", "EPP", "ECP+EPP", "ECP+PS/2", "ECP", - "UNKNOWN", 0 + "COMPATIBLE", "NIBBLE", "PS/2", "PS/2", "EPP", + "EPP", "EPP", "EPP", "ECP", + "ECP", "ECP+PS2", "ECP+PS2", "ECP+EPP", + "ECP+EPP", "ECP+EPP", "ECP+EPP", 0 }; static char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 }; @@ -110,12 +124,19 @@ static void ppc_wfifo(int unit, char byte) { w_fifo(ppcdata[unit], byte); } static void ppc_reset_epp_timeout(int); static void ppc_ecp_sync(int); +static int ppc_exec_microseq(int, struct ppb_microseq *, int *); +static int ppc_generic_setmode(int, int); + static struct ppb_adapter ppc_adapter = { 0, /* no intr handler, filled by chipset dependent code */ ppc_reset_epp_timeout, ppc_ecp_sync, + ppc_exec_microseq, + + ppc_generic_setmode, + ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -143,8 +164,8 @@ ppc_ecp_sync(int unit) { DELAY(100); } - printf("ppc: ECP sync failed as data still " \ - "present in FIFO.\n"); + printf("ppc%d: ECP sync failed as data still " \ + "present in FIFO.\n", unit); return; } @@ -158,6 +179,35 @@ ppcintr(int unit) return; } +static void +ppc_ecp_config(struct ppc_data *ppc, int chipset_mode) +{ + /* XXX disable DMA, enable interrupts */ + if (chipset_mode & PPB_EPP) + /* select EPP mode */ + w_ecr(ppc, 0x80); + else if (chipset_mode & PPB_PS2) + /* select PS2 mode with ECP */ + w_ecr(ppc, 0x20); + else + /* keep ECP mode alone, default for NIBBLE */ + w_ecr(ppc, 0x70); + + return; +} + +static int +ppc_detect_port(struct ppc_data *ppc) +{ + + w_ctr(ppc, 0x0c); /* To avoid missing PS2 ports */ + w_dtr(ppc, 0xaa); + if (r_dtr(ppc) != (char) 0xaa) + return (0); + + return (1); +} + /* * ppc_pc873xx_detect * @@ -170,11 +220,11 @@ static int pc873xx_basetab[] = {0x0398, 0x026e, 0x015c, 0x002e, 0}; static int pc873xx_porttab[] = {0x0378, 0x03bc, 0x0278, 0}; static int -ppc_pc873xx_detect(struct ppc_data *ppc) +ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never forced */ { static int index = 0; int base, idport; - int val, mode; + int val; while ((idport = pc873xx_basetab[index++])) { @@ -235,7 +285,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) printf("PC873xx locked\n"); /* work out what mode we're in */ - mode = PPB_NIBBLE; /* worst case */ + ppc->ppc_avm |= PPB_NIBBLE; /* worst case */ outb(idport, PC873_PCR); val = inb(idport + 1); @@ -243,10 +293,10 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport, PC873_PTR); val = inb(idport + 1); if (!(val & PC873_EPPRDIR)) { - mode = PPB_EPP; /* As we would have done it anwyay */ + ppc->ppc_avm |= PPB_EPP; /* As we would have done it anwyay */ } } else if ((val & PC873_ECPEN) && (val & PC873_ECPCLK)) { - mode = PPB_PS2; /* tolerable alternative */ + ppc->ppc_avm |= PPB_PS2; /* tolerable alternative */ } } else { if (bootverbose) @@ -299,7 +349,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, val); /* we are an EPP-32 port */ - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP; } else { if (bootverbose) printf("ECP\n"); @@ -309,44 +359,21 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, inb(idport + 1) | PC873_ECPEN | PC873_ECPCLK); /* we look like a PS/2 port */ - mode = PPB_PS2; + ppc->ppc_avm |= PPB_PS2; } } - return(mode); + + return(chipset_mode); } - return(0); + return(-1); } static int -ppc_detect_ps2(struct ppc_data *ppc) +ppc_check_epp_timeout(struct ppc_data *ppc) { - char save_control, r; - - save_control = r_ctr(ppc); - - /* Try PS/2 mode */ - w_ctr(ppc, 0xec); - w_dtr(ppc, 0x55); - - /* needed if in ECP mode */ - if (ppc->ppc_mode == PPB_ECP) - w_ctr(ppc, PCD | 0xec); - r = r_dtr(ppc); - - if (r != (char) 0xff) { - if (r != (char) 0x55) - return 0; - - w_dtr(ppc, 0xaa); - r = r_dtr(ppc); - if (r != (char) 0xaa) - return 0; - - return (PPB_NIBBLE); - } else - w_ctr(ppc, save_control); + ppc_reset_epp_timeout(ppc->ppc_unit); - return (PPB_PS2); + return (!(r_str(ppc) & TIMEOUT)); } /* @@ -355,11 +382,10 @@ ppc_detect_ps2(struct ppc_data *ppc) * SMC FDC37C66xGT configuration. */ static int -ppc_smc37c66xgt_detect(struct ppc_data *ppc, int mode) +ppc_smc37c66xgt_detect(struct ppc_data *ppc, int chipset_mode) { int s, i; char r; - int retry = 0; /* boolean */ int type = -1; int csr = SMC66x_CSR; /* initial value is 0x3F0 */ @@ -404,7 +430,7 @@ config: * If chipset not found, do not continue. */ if (type == -1) - return (0); + return (-1); /* select CR1 */ outb(csr, 0x1); @@ -412,65 +438,72 @@ config: /* read the port's address: bits 0 and 1 of CR1 */ r = inb(cio) & SMC_CR1_ADDR; if (port_address[r] != ppc->ppc_base) - return (0); + return (-1); ppc->ppc_type = type; /* * CR1 and CR4 registers bits 3 and 0/1 for mode configuration - * If SPP mode is detected, try to set ECP+EPP mode end retry - * detection to verify. + * If SPP mode is detected, try to set ECP+EPP mode */ -retry: - /* select CR1 register */ + if (bootverbose) { + outb(csr, 0x1); + printf("SMC registers CR1=0x%x", ppc->ppc_unit, + inb(cio) & 0xff); + + outb(csr, 0x4); + printf(" CR4=0x%x", inb(cio) & 0xff); + } + + /* select CR1 */ outb(csr, 0x1); - if (!mode) { + if (!chipset_mode) { /* autodetect mode */ - /* 666GT chipset is hardwired to an extended mode */ - if (type == SMC_37C666GT) - mode = PPB_ECP_EPP; + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ + if (type == SMC_37C666GT) { + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; - else if ((inb(cio) & SMC_CR1_MODE) == 0) { + } else + if ((inb(cio) & SMC_CR1_MODE) == 0) { /* already in extended parallel port mode, read CR4 */ outb(csr, 0x4); r = (inb(cio) & SMC_CR4_EMODE); switch (r) { case SMC_SPP: - /* let's detect NIBBLE or PS/2 later */ + ppc->ppc_avm |= PPB_SPP; break; case SMC_EPPSPP: - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP | PPB_SPP; break; case SMC_ECP: - /* - * Yet, don't know what to do with it! XXX - * So, consider ECP mode as PS/2. - * (see configuration later). - */ - mode = PPB_ECP; + ppc->ppc_avm |= PPB_ECP | PPB_SPP; break; case SMC_ECPEPP: - mode = PPB_ECP_EPP; + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; break; } - } + } else { + /* not an extended port mode */ + ppc->ppc_avm |= PPB_SPP; + } + } else { /* mode forced */ - /* 666GT chipset is hardwired to an extended mode */ + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ if (type == SMC_37C666GT) goto end_detect; r = inb(cio); - if (mode == PPB_NIBBLE || mode == PPB_PS2) { - /* do not use ECP when the mode is forced to SPP */ + if ((chipset_mode & (PPB_ECP | PPB_EPP)) == 0) { + /* do not use ECP when the mode is not forced to */ outb(cio, r | SMC_CR1_MODE); } else { /* an extended mode is selected */ @@ -480,30 +513,26 @@ retry: outb(csr, 0x4); r = inb(cio) & ~SMC_CR4_EMODE; - switch (mode) { - case PPB_EPP: + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) { + outb(cio, r | SMC_ECPEPP); + } else { + outb(cio, r | SMC_ECP); + } + } else { + /* PPB_EPP is set */ outb(cio, r | SMC_EPPSPP); - break; - - case PPB_ECP: - case PPB_ECP_PS2: - outb(cio, r | SMC_ECP); - break; - - case PPB_ECP_EPP: - outb(cio, r | SMC_ECPEPP); - break; - - default: - printf("ppc: unknown mode (%d)\n", - mode); - return (0); } } + ppc->ppc_avm = chipset_mode; } end_detect: - if (PPB_IS_EPP(mode)) { + + if (bootverbose) + printf ("\n"); + + if (chipset_mode & PPB_EPP) { /* select CR4 */ outb(csr, 0x4); r = inb(cio); @@ -511,11 +540,9 @@ end_detect: /* * Set the EPP protocol... * Low=EPP 1.9 (1284 standard) and High=EPP 1.7 - * ...then check the result. */ if (ppc->ppc_epp == EPP_1_9) outb(cio, (r & ~SMC_CR4_EPPTYPE)); - else outb(cio, (r | SMC_CR4_EPPTYPE)); } @@ -523,86 +550,214 @@ end_detect: /* end config mode */ outb(csr, 0xaa); - /* - * Write 100 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP_EPP) - w_ecr(ppc, 0x80); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - /* - * Write 001 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP) - w_ecr(ppc, 0x20); + return (chipset_mode); +} + +/* + * Winbond W83877F stuff + * + * EFER: extended function enable register + * EFIR: extended function index register + * EFDR: extended function data register + */ +#define efir ((efer == 0x250) ? 0x251 : 0x3f0) +#define efdr ((efer == 0x250) ? 0x252 : 0x3f1) + +static int w83877f_efers[] = { 0x250, 0x3f0, 0x3f0, 0x250 }; +static int w83877f_keys[] = { 0x89, 0x86, 0x87, 0x88 }; +static int w83877f_keyiter[] = { 1, 2, 2, 1 }; +static int w83877f_hefs[] = { WINB_HEFERE, WINB_HEFRAS, WINB_HEFERE | WINB_HEFRAS, 0 }; + +static int +ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode) +{ + int i, j, efer, base; + unsigned char r, hefere, hefras; + + for (i = 0; i < 4; i ++) { + /* first try to enable configuration registers */ + efer = w83877f_efers[i]; + + /* write the key to the EFER */ + for (j = 0; j < w83877f_keyiter[i]; j ++) + outb (efer, w83877f_keys[i]); + + /* then check HEFERE and HEFRAS bits */ + outb (efir, 0x0c); + hefere = inb(efdr) & WINB_HEFERE; + + outb (efir, 0x16); + hefras = inb(efdr) & WINB_HEFRAS; - if (PPB_IS_EPP(mode)) { /* - * Try to reset EPP timeout bit. - * If it fails, try PS/2 and NIBBLE modes. + * HEFRAS HEFERE + * 0 1 write 89h to 250h (power-on default) + * 1 0 write 86h twice to 3f0h + * 1 1 write 87h twice to 3f0h + * 0 0 write 88h to 250h */ - ppc_reset_epp_timeout(ppc->ppc_unit); + if ((hefere | hefras) == w83877f_hefs[i]) + goto found; + } - r = r_str(ppc); - if (!(r & TIMEOUT)) - return (mode); - } else { - if (mode) - return (mode); + return (-1); /* failed */ + +found: + /* check base port address - read from CR23 */ + outb(efir, 0x23); + if (ppc->ppc_base != inb(efdr) * 4) /* 4 bytes boundaries */ + return (-1); + + /* read CHIP ID from CR9/bits0-3 */ + outb(efir, 0x9); + + switch (inb(efdr) & WINB_CHIPID) { + case WINB_W83877F_ID: + ppc->ppc_type = WINB_W83877F; + break; + + case WINB_W83877AF_ID: + ppc->ppc_type = WINB_W83877AF; + break; + + default: + ppc->ppc_type = WINB_UNKNOWN; } - /* detect PS/2 or NIBBLE mode */ - return (ppc_detect_ps2(ppc)); -} + if (bootverbose) { + /* dump of registers */ + printf("ppc%d: 0x%x - ", ppc->ppc_unit, w83877f_keys[i]); + for (i = 0; i <= 0xd; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + for (i = 0x10; i <= 0x17; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + outb(efir, 0x1e); + printf("0x%x ", inb(efdr)); + for (i = 0x20; i <= 0x29; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + printf("\n"); + } -static int -ppc_check_ecpepp_timeout(struct ppc_data *ppc) -{ - char r; + if (!chipset_mode) { + /* autodetect mode */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* select CR0 */ + outb(efir, 0x0); + r = inb(efdr) & (WINB_PRTMODS0 | WINB_PRTMODS1); + + /* select CR9 */ + outb(efir, 0x9); + r |= (inb(efdr) & WINB_PRTMODS2); + + switch (r) { + case WINB_W83757: + if (bootverbose) + printf("ppc%d: W83757 compatible mode\n", + ppc->ppc_unit); + return (-1); /* generic or SMC-like */ + + case WINB_EXTFDC: + case WINB_EXTADP: + case WINB_EXT2FDD: + case WINB_JOYSTICK: + if (bootverbose) + printf("ppc%d: not in parallel port mode\n", + ppc->ppc_unit); + return (-1); + + case (WINB_PARALLEL | WINB_EPP_SPP): + ppc->ppc_avm |= PPB_EPP | PPB_SPP; + break; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_ECP_EPP); + case (WINB_PARALLEL | WINB_ECP): + ppc->ppc_avm |= PPB_ECP | PPB_SPP; + break; + + case (WINB_PARALLEL | WINB_ECP_EPP): + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; + break; + default: + printf("%s: unknown case (0x%x)!\n", __FUNCTION__, r); + } + + } else { + /* mode forced */ + + /* select CR9 and set PRTMODS2 bit */ + outb(efir, 0x9); + outb(efdr, inb(efdr) & ~WINB_PRTMODS2); + + /* select CR0 and reset PRTMODSx bits */ + outb(efir, 0x0); + outb(efdr, inb(efdr) & ~(WINB_PRTMODS0 | WINB_PRTMODS1)); + + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) + outb(efdr, inb(efdr) | WINB_ECP_EPP); + else + outb(efdr, inb(efdr) | WINB_ECP); + } else { + /* select EPP_SPP otherwise */ + outb(efdr, inb(efdr) | WINB_EPP_SPP); + } + ppc->ppc_avm = chipset_mode; } + + /* exit configuration mode */ + outb(efer, 0xaa); - /* If EPP timeout bit is not reset, DON'T use EPP */ - w_ecr(ppc, 0x20); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (PPB_ECP_PS2); + return (chipset_mode); } /* * ppc_generic_detect */ static int -ppc_generic_detect(struct ppc_data *ppc, int mode) +ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) { - char save_control, r; + char save_control; + + if (!chipset_mode) { + /* first, check for ECP */ + w_ecr(ppc, 0x20); + if ((r_ecr(ppc) & 0xe0) == 0x20) { + ppc->ppc_avm |= PPB_ECP | PPB_SPP; - /* don't know what to do here */ - if (mode) - return (mode); + /* search for SMC style ECP+EPP mode */ + w_ecr(ppc, 0x80); + } - /* try to reset EPP timeout bit */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* try to reset EPP timeout bit */ + if (ppc_check_epp_timeout(ppc)) { + ppc->ppc_avm |= PPB_EPP; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_EPP); - } + if (ppc->ppc_avm & PPB_ECP) + /* SMC like chipset found */ + ppc->ppc_type = SMC_LIKE; + } - /* Now check for ECP */ - w_ecr(ppc, 0x20); - r = r_ecr(ppc); - if ((r & 0xe0) == 0x20) { - /* Search for SMC style EPP+ECP mode */ - w_ecr(ppc, 0x80); + /* XXX try to detect NIBBLE mode */ + ppc->ppc_avm |= PPB_NIBBLE; - return (ppc_check_ecpepp_timeout(ppc)); - } + } else + ppc->ppc_avm = chipset_mode; + + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (ppc_detect_ps2(ppc)); + return (chipset_mode); } /* @@ -611,22 +766,258 @@ ppc_generic_detect(struct ppc_data *ppc, int mode) * mode is the mode suggested at boot */ static int -ppc_detect(struct ppc_data *ppc, int mode) { +ppc_detect(struct ppc_data *ppc, int chipset_mode) { - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_pc873xx_detect(ppc))) - goto end_detect; + int i, mode; - if (!ppc->ppc_mode && (ppc->ppc_mode = - ppc_smc37c66xgt_detect(ppc, mode))) - goto end_detect; + /* list of supported chipsets */ + int (*chipset_detect[])(struct ppc_data *, int) = { + ppc_pc873xx_detect, + ppc_smc37c66xgt_detect, + ppc_w83877f_detect, + ppc_generic_detect, + NULL + }; - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_generic_detect(ppc, mode))) - goto end_detect; + /* if can't find the port and mode not forced return error */ + if (!ppc_detect_port(ppc) && chipset_mode == 0) + return (EIO); /* failed, port not present */ - printf("ppc: port not present at 0x%x.\n", ppc->ppc_base); - return (PPC_ENOPORT); + /* assume centronics compatible mode is supported */ + ppc->ppc_avm = PPB_COMPATIBLE; -end_detect: + /* we have to differenciate available chipset modes, + * chipset running modes and IEEE-1284 operating modes + * + * after detection, the port must support running in compatible mode + */ + for (i=0; chipset_detect[i] != NULL; i++) { + if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) { + ppc->ppc_mode = mode; + break; + } + } + + return (0); +} + +/* + * ppc_exec_microseq() + * + * Execute a microsequence. + * Microsequence mechanism is supposed to handle fast I/O operations. + */ +static int +ppc_exec_microseq(int unit, struct ppb_microseq *msq, int *ppbpc) +{ + struct ppc_data *ppc = ppcdata[unit]; + struct ppb_microseq *pc; + char cc, *p; + int i, iter, reg; + int error; + + /* static to be reused after few ppc_exec_microseq()/return calls + * XXX should be in a context variable shared with ppb level */ + static int accum; + static char *ptr; + + struct ppb_microseq *microseq_stack = 0; + struct ppb_microseq *pc_stack = 0; + +/* microsequence registers are equivalent to PC-like port registers */ +#define r_reg(register,ppc) ((char)inb((ppc)->ppc_base + register)) +#define w_reg(register,ppc,byte) outb((ppc)->ppc_base + register, byte) + +#define INCR_PC (pc ++) /* increment program counter */ +#define mi pc /* microinstruction currently executed */ + + /* get the state of pc from ppb level of execution */ + pc = &msq[*ppbpc]; + + for (;;) { + + switch (mi->opcode) { + case MS_OP_RSET: + cc = r_reg(mi->arg[0].i, ppc); + cc &= mi->arg[2].c; /* clear mask */ + cc |= mi->arg[1].c; /* assert mask */ + w_reg(mi->arg[0].i, ppc, cc); + INCR_PC; + break; + + case MS_OP_RASSERT_P: + for (i=0; i<mi->arg[0].i; i++) + w_reg(mi->arg[1].i, ppc, *ptr++); + INCR_PC; + break; + + case MS_OP_RFETCH_P: + for (i=0; i<mi->arg[0].i; i++) + *ptr++ = r_reg(mi->arg[1].i, ppc) & + mi->arg[2].c; + INCR_PC; + break; + + case MS_OP_RFETCH: + *((char *) mi->arg[2].p) = r_reg(mi->arg[0].i, ppc) & + mi->arg[1].c; + INCR_PC; + break; + + case MS_OP_RASSERT: + + /* let's suppose the next instr. is the same */ + prefetch: + for (;mi->opcode == MS_OP_RASSERT; INCR_PC) + w_reg(mi->arg[0].i, ppc, mi->arg[1].c); + + if (mi->opcode == MS_OP_DELAY) { + DELAY(mi->arg[0].i); + INCR_PC; + goto prefetch; + } + break; + + case MS_OP_DELAY: + DELAY(mi->arg[0].i); + INCR_PC; + break; + + case MS_OP_TRIG: + reg = mi->arg[0].i; + iter = mi->arg[1].i; + p = (char *)mi->arg[2].p; + + for (i=0; i<iter; i++) { + w_reg(reg, ppc, *p++); + DELAY((unsigned char)*p++); + } + INCR_PC; + break; + + case MS_OP_SET: + accum = mi->arg[0].i; + INCR_PC; + break; + + case MS_OP_DBRA: + if (--accum > 0) + pc += mi->arg[0].i; + else + INCR_PC; + break; + + case MS_OP_BRSET: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == mi->arg[0].c) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_BRCLEAR: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == 0) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_C_CALL: + /* + * If the C call returns !0 then end the microseq. + * The current state of ptr is passed to the C function + */ + if ((error = mi->arg[0].f(mi->arg[1].p, ptr))) + return (error); + + INCR_PC; + break; + + case MS_OP_PTR: + ptr = (char *)mi->arg[0].p; + INCR_PC; + break; + + case MS_OP_CALL: + if (microseq_stack) + panic("%s: too much calls", __FUNCTION__); + + if (mi->arg[0].p) { + /* store the state of the actual + * microsequence + */ + microseq_stack = msq; + pc_stack = pc; + + /* jump to the new microsequence */ + msq = (struct ppb_microseq *)mi->arg[0].p; + pc = msq; + } else + INCR_PC; + + break; + + case MS_OP_SUBRET: + /* retrieve microseq and pc state before the call */ + msq = microseq_stack; + pc = pc_stack; + + /* reset the stack */ + microseq_stack = 0; + + /* XXX return code */ + + INCR_PC; + break; + + case MS_OP_PUT: + case MS_OP_GET: + case MS_OP_RET: + /* can't return to ppb level during the execution + * of a submicrosequence */ + if (microseq_stack) + panic("%s: can't return to ppb level", + __FUNCTION__); + + /* update pc for ppb level of execution */ + *ppbpc = (int)(pc - msq); + + /* return to ppb level of execution */ + return (0); + + default: + panic("%s: unknown microsequence opcode 0x%x", + __FUNCTION__, mi->opcode); + } + } + + /* unreached */ +} + +/* + * Configure current operating mode + */ +static int +ppc_generic_setmode(int unit, int mode) +{ + struct ppc_data *ppc = ppcdata[unit]; + + /* back to compatible mode, XXX don't know yet what to do here */ + if (mode == 0) { + ppc->ppc_mode = PPB_COMPATIBLE; + return (0); + } + + /* check if mode is available */ + if (!(ppc->ppc_avm & mode)) + return (EOPNOTSUPP); + + /* if ECP mode, configure ecr register */ + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, mode); + + ppc->ppc_mode = mode; return (0); } @@ -688,16 +1079,14 @@ ppcprobe(struct isa_device *dvp) ppc->ppc_unit = dvp->id_unit; ppc->ppc_type = GENERIC; - /* PPB_AUTODETECT is default to allow chipset detection even if - * mode is forced by dvp->id_flags (see later, ppc_detect() call) */ - ppc->ppc_mode = PPB_AUTODETECT; - ppc->ppc_epp = (dvp->id_flags & 0x8) >> 3; + ppc->ppc_mode = PPB_COMPATIBLE; + ppc->ppc_epp = (dvp->id_flags & 0x10) >> 4; /* * XXX * Try and detect if interrupts are working. */ - if (!(dvp->id_flags & 0x10)) + if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = (dvp->id_irq); ppcdata[ppc->ppc_unit] = ppc; @@ -706,7 +1095,7 @@ ppcprobe(struct isa_device *dvp) /* * Try to detect the chipset and its mode. */ - if (ppc_detect(ppc, dvp->id_flags & 0x7)) + if (ppc_detect(ppc, dvp->id_flags & 0xf)) goto error; end_probe: @@ -722,6 +1111,7 @@ ppcattach(struct isa_device *isdp) { struct ppc_data *ppc = ppcdata[isdp->id_unit]; struct ppb_data *ppbus; + char * mode; /* * Link the Parallel Port Chipset (adapter) to @@ -730,9 +1120,9 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.adapter_unit = ppc->ppc_unit; ppc->ppc_link.adapter = &ppc_adapter; - printf("ppc%d: %s chipset in %s mode%s\n", ppc->ppc_unit, - ppc_types[ppc->ppc_type], ppc_modes[ppc->ppc_mode], - (PPB_IS_EPP(ppc->ppc_mode)) ? + printf("ppc%d: %s chipset (%s) in %s mode%s\n", ppc->ppc_unit, + ppc_types[ppc->ppc_type], ppc_avms[ppc->ppc_avm], + ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); /* diff --git a/sys/i386/isa/ppcreg.h b/sys/i386/isa/ppcreg.h index 7ab6189e..27ab1a1 100644 --- a/sys/i386/isa/ppcreg.h +++ b/sys/i386/isa/ppcreg.h @@ -23,23 +23,25 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.1 1997/08/14 14:01:36 msmith Exp $ + * $Id: ppcreg.h,v 1.2 1997/08/16 14:07:26 msmith Exp $ * */ -#ifndef __PPC_H -#define __PPC_H +#ifndef __PPCREG_H +#define __PPCREG_H /* * Parallel Port Chipset type. */ -#define SMC_UNKNOWN 0x0 +#define SMC_LIKE 0x0 #define SMC_37C665GT 0x1 #define SMC_37C666GT 0x2 -#define NS_UNKNOWN 0x3 -#define NS_PC87332 0x4 -#define NS_PC87306 0x5 -#define INTEL_820191AA 0x6 -#define GENERIC 0x7 +#define NS_PC87332 0x3 +#define NS_PC87306 0x4 +#define INTEL_820191AA 0x5 /* XXX not implemented */ +#define GENERIC 0x6 +#define WINB_W83877F 0x7 +#define WINB_W83877AF 0x8 +#define WINB_UNKNOWN 0x9 /* * Generic structure to hold parallel port chipset info. @@ -49,10 +51,13 @@ struct ppc_data { int ppc_unit; int ppc_type; + int ppc_mode; /* chipset current mode */ + int ppc_avm; /* chipset available modes */ + #define ppc_base ppc_link.base -#define ppc_mode ppc_link.mode #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq +#define ppc_subm ppc_link.submicroseq unsigned char ppc_flags; @@ -60,11 +65,6 @@ struct ppc_data { }; /* - * Parallel Port Chipset errors. XXX - */ -#define PPC_ENOPORT 9 - -/* * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ @@ -74,12 +74,12 @@ struct ppc_data { #define PPC_ECP_FIFO 0x400 /* ECP fifo register */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ -#define r_dtr(ppc) inb((ppc)->ppc_base + PPC_SPP_DTR) -#define r_str(ppc) inb((ppc)->ppc_base + PPC_SPP_STR) -#define r_ctr(ppc) inb((ppc)->ppc_base + PPC_SPP_CTR) -#define r_epp(ppc) inb((ppc)->ppc_base + PPC_EPP_DATA) -#define r_ecr(ppc) inb((ppc)->ppc_base + PPC_ECP_ECR) -#define r_fifo(ppc) inb((ppc)->ppc_base + PPC_ECP_FIFO) +#define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) +#define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) +#define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) +#define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) @@ -111,7 +111,7 @@ struct ppc_data { #define PC873_SID 0x08 /* - * Register defines for the SMC FDC37C66xGT parts. + * Register defines for the SMC FDC37C66xGT parts */ /* Init codes */ @@ -124,9 +124,9 @@ struct ppc_data { /* Bits */ #define SMC_CR1_ADDR 0x3 /* bit 0 and 1 */ -#define SMC_CR1_MODE 0x8 /* bit 3 */ +#define SMC_CR1_MODE (1<<3) /* bit 3 */ #define SMC_CR4_EMODE 0x3 /* bits 0 and 1 */ -#define SMC_CR4_EPPTYPE 0x40 /* bit 6 */ +#define SMC_CR4_EPPTYPE (1<<6) /* bit 6 */ /* Extended modes */ #define SMC_SPP 0x0 /* SPP */ @@ -134,5 +134,34 @@ struct ppc_data { #define SMC_ECP 0x2 /* ECP */ #define SMC_ECPEPP 0x3 /* ECP and EPP */ -#endif +/* + * Register defines for the Winbond W83877F parts + */ +#define WINB_W83877F_ID 0xa +#define WINB_W83877AF_ID 0xb + +/* Configuration bits */ +#define WINB_HEFERE (1<<5) /* CROC bit 5 */ +#define WINB_HEFRAS (1<<0) /* CR16 bit 0 */ + +#define WINB_PNPCVS (1<<2) /* CR16 bit 2 */ +#define WINB_CHIPID 0xf /* CR9 bits 0-3 */ + +#define WINB_PRTMODS0 (1<<2) /* CR0 bit 2 */ +#define WINB_PRTMODS1 (1<<3) /* CR0 bit 3 */ +#define WINB_PRTMODS2 (1<<7) /* CR9 bit 7 */ + +/* W83877F modes: CR9/bit7 | CR0/bit3 | CR0/bit2 */ +#define WINB_W83757 0x0 +#define WINB_EXTFDC 0x4 +#define WINB_EXTADP 0x8 +#define WINB_EXT2FDD 0xc +#define WINB_JOYSTICK 0x80 + +#define WINB_PARALLEL 0x80 +#define WINB_EPP_SPP 0x4 +#define WINB_ECP 0x8 +#define WINB_ECP_EPP 0xc + +#endif diff --git a/sys/isa/ppc.c b/sys/isa/ppc.c index 39d0ece..71daa85 100644 --- a/sys/isa/ppc.c +++ b/sys/isa/ppc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997 Nicolas Souchu + * Copyright (c) 1997, 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.2 1997/09/01 02:08:41 bde Exp $ + * $Id: ppc.c,v 1.3 1998/04/17 22:36:37 des Exp $ * */ #include "ppc.h" @@ -44,6 +44,8 @@ #include <i386/isa/isa_device.h> #include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> + #include <i386/isa/ppcreg.h> static int ppcprobe(struct isa_device *); @@ -57,14 +59,26 @@ static struct ppc_data *ppcdata[NPPC]; static int nppc = 0; static char *ppc_types[] = { - "SMC", "SMC FDC37C665GT", "SMC FDC37C666GT", - "NatSemi", "PC87332", "PC87306", - "Intel 82091AA", "Generic", 0 + "SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306", + "82091AA", "Generic", "W83877F", "W83877AF", "Winbond", 0 +}; + +/* list of available modes */ +static char *ppc_avms[] = { + "COMPATIBLE", "NIBBLE-only", "PS2-only", "PS2/NIBBLE", "EPP-only", + "EPP/NIBBLE", "EPP/PS2", "EPP/PS2/NIBBLE", "ECP-only", + "ECP/NIBBLE", "ECP/PS2", "ECP/PS2/NIBBLE", "ECP/EPP", + "ECP/EPP/NIBBLE", "ECP/EPP/PS2", "ECP/EPP/PS2/NIBBLE", 0 }; +/* list of current executing modes + * Note that few modes do not actually exist. + */ static char *ppc_modes[] = { - "AUTODETECT", "NIBBLE", "PS/2", "EPP", "ECP+EPP", "ECP+PS/2", "ECP", - "UNKNOWN", 0 + "COMPATIBLE", "NIBBLE", "PS/2", "PS/2", "EPP", + "EPP", "EPP", "EPP", "ECP", + "ECP", "ECP+PS2", "ECP+PS2", "ECP+EPP", + "ECP+EPP", "ECP+EPP", "ECP+EPP", 0 }; static char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 }; @@ -110,12 +124,19 @@ static void ppc_wfifo(int unit, char byte) { w_fifo(ppcdata[unit], byte); } static void ppc_reset_epp_timeout(int); static void ppc_ecp_sync(int); +static int ppc_exec_microseq(int, struct ppb_microseq *, int *); +static int ppc_generic_setmode(int, int); + static struct ppb_adapter ppc_adapter = { 0, /* no intr handler, filled by chipset dependent code */ ppc_reset_epp_timeout, ppc_ecp_sync, + ppc_exec_microseq, + + ppc_generic_setmode, + ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -143,8 +164,8 @@ ppc_ecp_sync(int unit) { DELAY(100); } - printf("ppc: ECP sync failed as data still " \ - "present in FIFO.\n"); + printf("ppc%d: ECP sync failed as data still " \ + "present in FIFO.\n", unit); return; } @@ -158,6 +179,35 @@ ppcintr(int unit) return; } +static void +ppc_ecp_config(struct ppc_data *ppc, int chipset_mode) +{ + /* XXX disable DMA, enable interrupts */ + if (chipset_mode & PPB_EPP) + /* select EPP mode */ + w_ecr(ppc, 0x80); + else if (chipset_mode & PPB_PS2) + /* select PS2 mode with ECP */ + w_ecr(ppc, 0x20); + else + /* keep ECP mode alone, default for NIBBLE */ + w_ecr(ppc, 0x70); + + return; +} + +static int +ppc_detect_port(struct ppc_data *ppc) +{ + + w_ctr(ppc, 0x0c); /* To avoid missing PS2 ports */ + w_dtr(ppc, 0xaa); + if (r_dtr(ppc) != (char) 0xaa) + return (0); + + return (1); +} + /* * ppc_pc873xx_detect * @@ -170,11 +220,11 @@ static int pc873xx_basetab[] = {0x0398, 0x026e, 0x015c, 0x002e, 0}; static int pc873xx_porttab[] = {0x0378, 0x03bc, 0x0278, 0}; static int -ppc_pc873xx_detect(struct ppc_data *ppc) +ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode) /* XXX mode never forced */ { static int index = 0; int base, idport; - int val, mode; + int val; while ((idport = pc873xx_basetab[index++])) { @@ -235,7 +285,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) printf("PC873xx locked\n"); /* work out what mode we're in */ - mode = PPB_NIBBLE; /* worst case */ + ppc->ppc_avm |= PPB_NIBBLE; /* worst case */ outb(idport, PC873_PCR); val = inb(idport + 1); @@ -243,10 +293,10 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport, PC873_PTR); val = inb(idport + 1); if (!(val & PC873_EPPRDIR)) { - mode = PPB_EPP; /* As we would have done it anwyay */ + ppc->ppc_avm |= PPB_EPP; /* As we would have done it anwyay */ } } else if ((val & PC873_ECPEN) && (val & PC873_ECPCLK)) { - mode = PPB_PS2; /* tolerable alternative */ + ppc->ppc_avm |= PPB_PS2; /* tolerable alternative */ } } else { if (bootverbose) @@ -299,7 +349,7 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, val); /* we are an EPP-32 port */ - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP; } else { if (bootverbose) printf("ECP\n"); @@ -309,44 +359,21 @@ ppc_pc873xx_detect(struct ppc_data *ppc) outb(idport + 1, inb(idport + 1) | PC873_ECPEN | PC873_ECPCLK); /* we look like a PS/2 port */ - mode = PPB_PS2; + ppc->ppc_avm |= PPB_PS2; } } - return(mode); + + return(chipset_mode); } - return(0); + return(-1); } static int -ppc_detect_ps2(struct ppc_data *ppc) +ppc_check_epp_timeout(struct ppc_data *ppc) { - char save_control, r; - - save_control = r_ctr(ppc); - - /* Try PS/2 mode */ - w_ctr(ppc, 0xec); - w_dtr(ppc, 0x55); - - /* needed if in ECP mode */ - if (ppc->ppc_mode == PPB_ECP) - w_ctr(ppc, PCD | 0xec); - r = r_dtr(ppc); - - if (r != (char) 0xff) { - if (r != (char) 0x55) - return 0; - - w_dtr(ppc, 0xaa); - r = r_dtr(ppc); - if (r != (char) 0xaa) - return 0; - - return (PPB_NIBBLE); - } else - w_ctr(ppc, save_control); + ppc_reset_epp_timeout(ppc->ppc_unit); - return (PPB_PS2); + return (!(r_str(ppc) & TIMEOUT)); } /* @@ -355,11 +382,10 @@ ppc_detect_ps2(struct ppc_data *ppc) * SMC FDC37C66xGT configuration. */ static int -ppc_smc37c66xgt_detect(struct ppc_data *ppc, int mode) +ppc_smc37c66xgt_detect(struct ppc_data *ppc, int chipset_mode) { int s, i; char r; - int retry = 0; /* boolean */ int type = -1; int csr = SMC66x_CSR; /* initial value is 0x3F0 */ @@ -404,7 +430,7 @@ config: * If chipset not found, do not continue. */ if (type == -1) - return (0); + return (-1); /* select CR1 */ outb(csr, 0x1); @@ -412,65 +438,72 @@ config: /* read the port's address: bits 0 and 1 of CR1 */ r = inb(cio) & SMC_CR1_ADDR; if (port_address[r] != ppc->ppc_base) - return (0); + return (-1); ppc->ppc_type = type; /* * CR1 and CR4 registers bits 3 and 0/1 for mode configuration - * If SPP mode is detected, try to set ECP+EPP mode end retry - * detection to verify. + * If SPP mode is detected, try to set ECP+EPP mode */ -retry: - /* select CR1 register */ + if (bootverbose) { + outb(csr, 0x1); + printf("SMC registers CR1=0x%x", ppc->ppc_unit, + inb(cio) & 0xff); + + outb(csr, 0x4); + printf(" CR4=0x%x", inb(cio) & 0xff); + } + + /* select CR1 */ outb(csr, 0x1); - if (!mode) { + if (!chipset_mode) { /* autodetect mode */ - /* 666GT chipset is hardwired to an extended mode */ - if (type == SMC_37C666GT) - mode = PPB_ECP_EPP; + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ + if (type == SMC_37C666GT) { + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; - else if ((inb(cio) & SMC_CR1_MODE) == 0) { + } else + if ((inb(cio) & SMC_CR1_MODE) == 0) { /* already in extended parallel port mode, read CR4 */ outb(csr, 0x4); r = (inb(cio) & SMC_CR4_EMODE); switch (r) { case SMC_SPP: - /* let's detect NIBBLE or PS/2 later */ + ppc->ppc_avm |= PPB_SPP; break; case SMC_EPPSPP: - mode = PPB_EPP; + ppc->ppc_avm |= PPB_EPP | PPB_SPP; break; case SMC_ECP: - /* - * Yet, don't know what to do with it! XXX - * So, consider ECP mode as PS/2. - * (see configuration later). - */ - mode = PPB_ECP; + ppc->ppc_avm |= PPB_ECP | PPB_SPP; break; case SMC_ECPEPP: - mode = PPB_ECP_EPP; + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; break; } - } + } else { + /* not an extended port mode */ + ppc->ppc_avm |= PPB_SPP; + } + } else { /* mode forced */ - /* 666GT chipset is hardwired to an extended mode */ + /* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */ if (type == SMC_37C666GT) goto end_detect; r = inb(cio); - if (mode == PPB_NIBBLE || mode == PPB_PS2) { - /* do not use ECP when the mode is forced to SPP */ + if ((chipset_mode & (PPB_ECP | PPB_EPP)) == 0) { + /* do not use ECP when the mode is not forced to */ outb(cio, r | SMC_CR1_MODE); } else { /* an extended mode is selected */ @@ -480,30 +513,26 @@ retry: outb(csr, 0x4); r = inb(cio) & ~SMC_CR4_EMODE; - switch (mode) { - case PPB_EPP: + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) { + outb(cio, r | SMC_ECPEPP); + } else { + outb(cio, r | SMC_ECP); + } + } else { + /* PPB_EPP is set */ outb(cio, r | SMC_EPPSPP); - break; - - case PPB_ECP: - case PPB_ECP_PS2: - outb(cio, r | SMC_ECP); - break; - - case PPB_ECP_EPP: - outb(cio, r | SMC_ECPEPP); - break; - - default: - printf("ppc: unknown mode (%d)\n", - mode); - return (0); } } + ppc->ppc_avm = chipset_mode; } end_detect: - if (PPB_IS_EPP(mode)) { + + if (bootverbose) + printf ("\n"); + + if (chipset_mode & PPB_EPP) { /* select CR4 */ outb(csr, 0x4); r = inb(cio); @@ -511,11 +540,9 @@ end_detect: /* * Set the EPP protocol... * Low=EPP 1.9 (1284 standard) and High=EPP 1.7 - * ...then check the result. */ if (ppc->ppc_epp == EPP_1_9) outb(cio, (r & ~SMC_CR4_EPPTYPE)); - else outb(cio, (r | SMC_CR4_EPPTYPE)); } @@ -523,86 +550,214 @@ end_detect: /* end config mode */ outb(csr, 0xaa); - /* - * Write 100 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP_EPP) - w_ecr(ppc, 0x80); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - /* - * Write 001 to the mode bits and disable DMA, enable intr. - */ - if (mode == PPB_ECP) - w_ecr(ppc, 0x20); + return (chipset_mode); +} + +/* + * Winbond W83877F stuff + * + * EFER: extended function enable register + * EFIR: extended function index register + * EFDR: extended function data register + */ +#define efir ((efer == 0x250) ? 0x251 : 0x3f0) +#define efdr ((efer == 0x250) ? 0x252 : 0x3f1) + +static int w83877f_efers[] = { 0x250, 0x3f0, 0x3f0, 0x250 }; +static int w83877f_keys[] = { 0x89, 0x86, 0x87, 0x88 }; +static int w83877f_keyiter[] = { 1, 2, 2, 1 }; +static int w83877f_hefs[] = { WINB_HEFERE, WINB_HEFRAS, WINB_HEFERE | WINB_HEFRAS, 0 }; + +static int +ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode) +{ + int i, j, efer, base; + unsigned char r, hefere, hefras; + + for (i = 0; i < 4; i ++) { + /* first try to enable configuration registers */ + efer = w83877f_efers[i]; + + /* write the key to the EFER */ + for (j = 0; j < w83877f_keyiter[i]; j ++) + outb (efer, w83877f_keys[i]); + + /* then check HEFERE and HEFRAS bits */ + outb (efir, 0x0c); + hefere = inb(efdr) & WINB_HEFERE; + + outb (efir, 0x16); + hefras = inb(efdr) & WINB_HEFRAS; - if (PPB_IS_EPP(mode)) { /* - * Try to reset EPP timeout bit. - * If it fails, try PS/2 and NIBBLE modes. + * HEFRAS HEFERE + * 0 1 write 89h to 250h (power-on default) + * 1 0 write 86h twice to 3f0h + * 1 1 write 87h twice to 3f0h + * 0 0 write 88h to 250h */ - ppc_reset_epp_timeout(ppc->ppc_unit); + if ((hefere | hefras) == w83877f_hefs[i]) + goto found; + } - r = r_str(ppc); - if (!(r & TIMEOUT)) - return (mode); - } else { - if (mode) - return (mode); + return (-1); /* failed */ + +found: + /* check base port address - read from CR23 */ + outb(efir, 0x23); + if (ppc->ppc_base != inb(efdr) * 4) /* 4 bytes boundaries */ + return (-1); + + /* read CHIP ID from CR9/bits0-3 */ + outb(efir, 0x9); + + switch (inb(efdr) & WINB_CHIPID) { + case WINB_W83877F_ID: + ppc->ppc_type = WINB_W83877F; + break; + + case WINB_W83877AF_ID: + ppc->ppc_type = WINB_W83877AF; + break; + + default: + ppc->ppc_type = WINB_UNKNOWN; } - /* detect PS/2 or NIBBLE mode */ - return (ppc_detect_ps2(ppc)); -} + if (bootverbose) { + /* dump of registers */ + printf("ppc%d: 0x%x - ", ppc->ppc_unit, w83877f_keys[i]); + for (i = 0; i <= 0xd; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + for (i = 0x10; i <= 0x17; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + outb(efir, 0x1e); + printf("0x%x ", inb(efdr)); + for (i = 0x20; i <= 0x29; i ++) { + outb(efir, i); + printf("0x%x ", inb(efdr)); + } + printf("\n"); + } -static int -ppc_check_ecpepp_timeout(struct ppc_data *ppc) -{ - char r; + if (!chipset_mode) { + /* autodetect mode */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* select CR0 */ + outb(efir, 0x0); + r = inb(efdr) & (WINB_PRTMODS0 | WINB_PRTMODS1); + + /* select CR9 */ + outb(efir, 0x9); + r |= (inb(efdr) & WINB_PRTMODS2); + + switch (r) { + case WINB_W83757: + if (bootverbose) + printf("ppc%d: W83757 compatible mode\n", + ppc->ppc_unit); + return (-1); /* generic or SMC-like */ + + case WINB_EXTFDC: + case WINB_EXTADP: + case WINB_EXT2FDD: + case WINB_JOYSTICK: + if (bootverbose) + printf("ppc%d: not in parallel port mode\n", + ppc->ppc_unit); + return (-1); + + case (WINB_PARALLEL | WINB_EPP_SPP): + ppc->ppc_avm |= PPB_EPP | PPB_SPP; + break; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_ECP_EPP); + case (WINB_PARALLEL | WINB_ECP): + ppc->ppc_avm |= PPB_ECP | PPB_SPP; + break; + + case (WINB_PARALLEL | WINB_ECP_EPP): + ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP; + break; + default: + printf("%s: unknown case (0x%x)!\n", __FUNCTION__, r); + } + + } else { + /* mode forced */ + + /* select CR9 and set PRTMODS2 bit */ + outb(efir, 0x9); + outb(efdr, inb(efdr) & ~WINB_PRTMODS2); + + /* select CR0 and reset PRTMODSx bits */ + outb(efir, 0x0); + outb(efdr, inb(efdr) & ~(WINB_PRTMODS0 | WINB_PRTMODS1)); + + if (chipset_mode & PPB_ECP) { + if (chipset_mode & PPB_EPP) + outb(efdr, inb(efdr) | WINB_ECP_EPP); + else + outb(efdr, inb(efdr) | WINB_ECP); + } else { + /* select EPP_SPP otherwise */ + outb(efdr, inb(efdr) | WINB_EPP_SPP); + } + ppc->ppc_avm = chipset_mode; } + + /* exit configuration mode */ + outb(efer, 0xaa); - /* If EPP timeout bit is not reset, DON'T use EPP */ - w_ecr(ppc, 0x20); + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (PPB_ECP_PS2); + return (chipset_mode); } /* * ppc_generic_detect */ static int -ppc_generic_detect(struct ppc_data *ppc, int mode) +ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) { - char save_control, r; + char save_control; + + if (!chipset_mode) { + /* first, check for ECP */ + w_ecr(ppc, 0x20); + if ((r_ecr(ppc) & 0xe0) == 0x20) { + ppc->ppc_avm |= PPB_ECP | PPB_SPP; - /* don't know what to do here */ - if (mode) - return (mode); + /* search for SMC style ECP+EPP mode */ + w_ecr(ppc, 0x80); + } - /* try to reset EPP timeout bit */ - ppc_reset_epp_timeout(ppc->ppc_unit); + /* try to reset EPP timeout bit */ + if (ppc_check_epp_timeout(ppc)) { + ppc->ppc_avm |= PPB_EPP; - r = r_str(ppc); - if (!(r & TIMEOUT)) { - return (PPB_EPP); - } + if (ppc->ppc_avm & PPB_ECP) + /* SMC like chipset found */ + ppc->ppc_type = SMC_LIKE; + } - /* Now check for ECP */ - w_ecr(ppc, 0x20); - r = r_ecr(ppc); - if ((r & 0xe0) == 0x20) { - /* Search for SMC style EPP+ECP mode */ - w_ecr(ppc, 0x80); + /* XXX try to detect NIBBLE mode */ + ppc->ppc_avm |= PPB_NIBBLE; - return (ppc_check_ecpepp_timeout(ppc)); - } + } else + ppc->ppc_avm = chipset_mode; + + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, chipset_mode); - return (ppc_detect_ps2(ppc)); + return (chipset_mode); } /* @@ -611,22 +766,258 @@ ppc_generic_detect(struct ppc_data *ppc, int mode) * mode is the mode suggested at boot */ static int -ppc_detect(struct ppc_data *ppc, int mode) { +ppc_detect(struct ppc_data *ppc, int chipset_mode) { - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_pc873xx_detect(ppc))) - goto end_detect; + int i, mode; - if (!ppc->ppc_mode && (ppc->ppc_mode = - ppc_smc37c66xgt_detect(ppc, mode))) - goto end_detect; + /* list of supported chipsets */ + int (*chipset_detect[])(struct ppc_data *, int) = { + ppc_pc873xx_detect, + ppc_smc37c66xgt_detect, + ppc_w83877f_detect, + ppc_generic_detect, + NULL + }; - if (!ppc->ppc_mode && (ppc->ppc_mode = ppc_generic_detect(ppc, mode))) - goto end_detect; + /* if can't find the port and mode not forced return error */ + if (!ppc_detect_port(ppc) && chipset_mode == 0) + return (EIO); /* failed, port not present */ - printf("ppc: port not present at 0x%x.\n", ppc->ppc_base); - return (PPC_ENOPORT); + /* assume centronics compatible mode is supported */ + ppc->ppc_avm = PPB_COMPATIBLE; -end_detect: + /* we have to differenciate available chipset modes, + * chipset running modes and IEEE-1284 operating modes + * + * after detection, the port must support running in compatible mode + */ + for (i=0; chipset_detect[i] != NULL; i++) { + if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) { + ppc->ppc_mode = mode; + break; + } + } + + return (0); +} + +/* + * ppc_exec_microseq() + * + * Execute a microsequence. + * Microsequence mechanism is supposed to handle fast I/O operations. + */ +static int +ppc_exec_microseq(int unit, struct ppb_microseq *msq, int *ppbpc) +{ + struct ppc_data *ppc = ppcdata[unit]; + struct ppb_microseq *pc; + char cc, *p; + int i, iter, reg; + int error; + + /* static to be reused after few ppc_exec_microseq()/return calls + * XXX should be in a context variable shared with ppb level */ + static int accum; + static char *ptr; + + struct ppb_microseq *microseq_stack = 0; + struct ppb_microseq *pc_stack = 0; + +/* microsequence registers are equivalent to PC-like port registers */ +#define r_reg(register,ppc) ((char)inb((ppc)->ppc_base + register)) +#define w_reg(register,ppc,byte) outb((ppc)->ppc_base + register, byte) + +#define INCR_PC (pc ++) /* increment program counter */ +#define mi pc /* microinstruction currently executed */ + + /* get the state of pc from ppb level of execution */ + pc = &msq[*ppbpc]; + + for (;;) { + + switch (mi->opcode) { + case MS_OP_RSET: + cc = r_reg(mi->arg[0].i, ppc); + cc &= mi->arg[2].c; /* clear mask */ + cc |= mi->arg[1].c; /* assert mask */ + w_reg(mi->arg[0].i, ppc, cc); + INCR_PC; + break; + + case MS_OP_RASSERT_P: + for (i=0; i<mi->arg[0].i; i++) + w_reg(mi->arg[1].i, ppc, *ptr++); + INCR_PC; + break; + + case MS_OP_RFETCH_P: + for (i=0; i<mi->arg[0].i; i++) + *ptr++ = r_reg(mi->arg[1].i, ppc) & + mi->arg[2].c; + INCR_PC; + break; + + case MS_OP_RFETCH: + *((char *) mi->arg[2].p) = r_reg(mi->arg[0].i, ppc) & + mi->arg[1].c; + INCR_PC; + break; + + case MS_OP_RASSERT: + + /* let's suppose the next instr. is the same */ + prefetch: + for (;mi->opcode == MS_OP_RASSERT; INCR_PC) + w_reg(mi->arg[0].i, ppc, mi->arg[1].c); + + if (mi->opcode == MS_OP_DELAY) { + DELAY(mi->arg[0].i); + INCR_PC; + goto prefetch; + } + break; + + case MS_OP_DELAY: + DELAY(mi->arg[0].i); + INCR_PC; + break; + + case MS_OP_TRIG: + reg = mi->arg[0].i; + iter = mi->arg[1].i; + p = (char *)mi->arg[2].p; + + for (i=0; i<iter; i++) { + w_reg(reg, ppc, *p++); + DELAY((unsigned char)*p++); + } + INCR_PC; + break; + + case MS_OP_SET: + accum = mi->arg[0].i; + INCR_PC; + break; + + case MS_OP_DBRA: + if (--accum > 0) + pc += mi->arg[0].i; + else + INCR_PC; + break; + + case MS_OP_BRSET: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == mi->arg[0].c) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_BRCLEAR: + cc = r_str(ppc); + if ((cc & mi->arg[0].c) == 0) + pc += mi->arg[1].i; + else + INCR_PC; + break; + + case MS_OP_C_CALL: + /* + * If the C call returns !0 then end the microseq. + * The current state of ptr is passed to the C function + */ + if ((error = mi->arg[0].f(mi->arg[1].p, ptr))) + return (error); + + INCR_PC; + break; + + case MS_OP_PTR: + ptr = (char *)mi->arg[0].p; + INCR_PC; + break; + + case MS_OP_CALL: + if (microseq_stack) + panic("%s: too much calls", __FUNCTION__); + + if (mi->arg[0].p) { + /* store the state of the actual + * microsequence + */ + microseq_stack = msq; + pc_stack = pc; + + /* jump to the new microsequence */ + msq = (struct ppb_microseq *)mi->arg[0].p; + pc = msq; + } else + INCR_PC; + + break; + + case MS_OP_SUBRET: + /* retrieve microseq and pc state before the call */ + msq = microseq_stack; + pc = pc_stack; + + /* reset the stack */ + microseq_stack = 0; + + /* XXX return code */ + + INCR_PC; + break; + + case MS_OP_PUT: + case MS_OP_GET: + case MS_OP_RET: + /* can't return to ppb level during the execution + * of a submicrosequence */ + if (microseq_stack) + panic("%s: can't return to ppb level", + __FUNCTION__); + + /* update pc for ppb level of execution */ + *ppbpc = (int)(pc - msq); + + /* return to ppb level of execution */ + return (0); + + default: + panic("%s: unknown microsequence opcode 0x%x", + __FUNCTION__, mi->opcode); + } + } + + /* unreached */ +} + +/* + * Configure current operating mode + */ +static int +ppc_generic_setmode(int unit, int mode) +{ + struct ppc_data *ppc = ppcdata[unit]; + + /* back to compatible mode, XXX don't know yet what to do here */ + if (mode == 0) { + ppc->ppc_mode = PPB_COMPATIBLE; + return (0); + } + + /* check if mode is available */ + if (!(ppc->ppc_avm & mode)) + return (EOPNOTSUPP); + + /* if ECP mode, configure ecr register */ + if (ppc->ppc_avm & PPB_ECP) + ppc_ecp_config(ppc, mode); + + ppc->ppc_mode = mode; return (0); } @@ -688,16 +1079,14 @@ ppcprobe(struct isa_device *dvp) ppc->ppc_unit = dvp->id_unit; ppc->ppc_type = GENERIC; - /* PPB_AUTODETECT is default to allow chipset detection even if - * mode is forced by dvp->id_flags (see later, ppc_detect() call) */ - ppc->ppc_mode = PPB_AUTODETECT; - ppc->ppc_epp = (dvp->id_flags & 0x8) >> 3; + ppc->ppc_mode = PPB_COMPATIBLE; + ppc->ppc_epp = (dvp->id_flags & 0x10) >> 4; /* * XXX * Try and detect if interrupts are working. */ - if (!(dvp->id_flags & 0x10)) + if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = (dvp->id_irq); ppcdata[ppc->ppc_unit] = ppc; @@ -706,7 +1095,7 @@ ppcprobe(struct isa_device *dvp) /* * Try to detect the chipset and its mode. */ - if (ppc_detect(ppc, dvp->id_flags & 0x7)) + if (ppc_detect(ppc, dvp->id_flags & 0xf)) goto error; end_probe: @@ -722,6 +1111,7 @@ ppcattach(struct isa_device *isdp) { struct ppc_data *ppc = ppcdata[isdp->id_unit]; struct ppb_data *ppbus; + char * mode; /* * Link the Parallel Port Chipset (adapter) to @@ -730,9 +1120,9 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.adapter_unit = ppc->ppc_unit; ppc->ppc_link.adapter = &ppc_adapter; - printf("ppc%d: %s chipset in %s mode%s\n", ppc->ppc_unit, - ppc_types[ppc->ppc_type], ppc_modes[ppc->ppc_mode], - (PPB_IS_EPP(ppc->ppc_mode)) ? + printf("ppc%d: %s chipset (%s) in %s mode%s\n", ppc->ppc_unit, + ppc_types[ppc->ppc_type], ppc_avms[ppc->ppc_avm], + ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); /* diff --git a/sys/isa/ppcreg.h b/sys/isa/ppcreg.h index 7ab6189e..27ab1a1 100644 --- a/sys/isa/ppcreg.h +++ b/sys/isa/ppcreg.h @@ -23,23 +23,25 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.1 1997/08/14 14:01:36 msmith Exp $ + * $Id: ppcreg.h,v 1.2 1997/08/16 14:07:26 msmith Exp $ * */ -#ifndef __PPC_H -#define __PPC_H +#ifndef __PPCREG_H +#define __PPCREG_H /* * Parallel Port Chipset type. */ -#define SMC_UNKNOWN 0x0 +#define SMC_LIKE 0x0 #define SMC_37C665GT 0x1 #define SMC_37C666GT 0x2 -#define NS_UNKNOWN 0x3 -#define NS_PC87332 0x4 -#define NS_PC87306 0x5 -#define INTEL_820191AA 0x6 -#define GENERIC 0x7 +#define NS_PC87332 0x3 +#define NS_PC87306 0x4 +#define INTEL_820191AA 0x5 /* XXX not implemented */ +#define GENERIC 0x6 +#define WINB_W83877F 0x7 +#define WINB_W83877AF 0x8 +#define WINB_UNKNOWN 0x9 /* * Generic structure to hold parallel port chipset info. @@ -49,10 +51,13 @@ struct ppc_data { int ppc_unit; int ppc_type; + int ppc_mode; /* chipset current mode */ + int ppc_avm; /* chipset available modes */ + #define ppc_base ppc_link.base -#define ppc_mode ppc_link.mode #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq +#define ppc_subm ppc_link.submicroseq unsigned char ppc_flags; @@ -60,11 +65,6 @@ struct ppc_data { }; /* - * Parallel Port Chipset errors. XXX - */ -#define PPC_ENOPORT 9 - -/* * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ @@ -74,12 +74,12 @@ struct ppc_data { #define PPC_ECP_FIFO 0x400 /* ECP fifo register */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ -#define r_dtr(ppc) inb((ppc)->ppc_base + PPC_SPP_DTR) -#define r_str(ppc) inb((ppc)->ppc_base + PPC_SPP_STR) -#define r_ctr(ppc) inb((ppc)->ppc_base + PPC_SPP_CTR) -#define r_epp(ppc) inb((ppc)->ppc_base + PPC_EPP_DATA) -#define r_ecr(ppc) inb((ppc)->ppc_base + PPC_ECP_ECR) -#define r_fifo(ppc) inb((ppc)->ppc_base + PPC_ECP_FIFO) +#define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) +#define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) +#define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) +#define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) @@ -111,7 +111,7 @@ struct ppc_data { #define PC873_SID 0x08 /* - * Register defines for the SMC FDC37C66xGT parts. + * Register defines for the SMC FDC37C66xGT parts */ /* Init codes */ @@ -124,9 +124,9 @@ struct ppc_data { /* Bits */ #define SMC_CR1_ADDR 0x3 /* bit 0 and 1 */ -#define SMC_CR1_MODE 0x8 /* bit 3 */ +#define SMC_CR1_MODE (1<<3) /* bit 3 */ #define SMC_CR4_EMODE 0x3 /* bits 0 and 1 */ -#define SMC_CR4_EPPTYPE 0x40 /* bit 6 */ +#define SMC_CR4_EPPTYPE (1<<6) /* bit 6 */ /* Extended modes */ #define SMC_SPP 0x0 /* SPP */ @@ -134,5 +134,34 @@ struct ppc_data { #define SMC_ECP 0x2 /* ECP */ #define SMC_ECPEPP 0x3 /* ECP and EPP */ -#endif +/* + * Register defines for the Winbond W83877F parts + */ +#define WINB_W83877F_ID 0xa +#define WINB_W83877AF_ID 0xb + +/* Configuration bits */ +#define WINB_HEFERE (1<<5) /* CROC bit 5 */ +#define WINB_HEFRAS (1<<0) /* CR16 bit 0 */ + +#define WINB_PNPCVS (1<<2) /* CR16 bit 2 */ +#define WINB_CHIPID 0xf /* CR9 bits 0-3 */ + +#define WINB_PRTMODS0 (1<<2) /* CR0 bit 2 */ +#define WINB_PRTMODS1 (1<<3) /* CR0 bit 3 */ +#define WINB_PRTMODS2 (1<<7) /* CR9 bit 7 */ + +/* W83877F modes: CR9/bit7 | CR0/bit3 | CR0/bit2 */ +#define WINB_W83757 0x0 +#define WINB_EXTFDC 0x4 +#define WINB_EXTADP 0x8 +#define WINB_EXT2FDD 0xc +#define WINB_JOYSTICK 0x80 + +#define WINB_PARALLEL 0x80 +#define WINB_EPP_SPP 0x4 +#define WINB_ECP 0x8 +#define WINB_ECP_EPP 0xc + +#endif |