Major ppbus updates from the author.

- ppbus now supports PLIP via the if_plip driver
 - ieee1284 infrastructure added, including parallel-port PnP
 - port microsequencer added, for scripting the sort of port I/O
   that is common with parallel devices without endless calls up and down
   through the driver structure.
 - improved bus ownership behaviour among the ppbus-using drivers.
 - improved I/O chipset feature detection

The vpo driver is now implemented using the microsequencer, leading to
some performance improvements as well as providing an extensive example
of its use.

Reviewed by:	msmith
Submitted by:	Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
This commit is contained in:
Mike Smith 1998-08-03 19:14:33 +00:00
parent 90afb6a523
commit 46f3ff7986
23 changed files with 4298 additions and 1373 deletions

View file

@ -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?

View file

@ -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

772
sys/dev/ppbus/if_plip.c Normal file
View file

@ -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;
}

View file

@ -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);

View file

@ -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,41 +38,79 @@
#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));
}
#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);

View file

@ -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

View file

@ -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>
@ -97,10 +97,30 @@ ppb_poll_device(struct ppb_device *dev, int max,
return (EWOULDBLOCK);
}
/*
* ppb_set_mode()
*
* Set the operating mode of the chipset
*/
int
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_link->adapter->setmode)(dev->id_unit, mode))
return (-1);
/* XXX yet device mode = ppbus mode = chipset mode */
dev->mode = ppb->mode = mode;
return (old_mode);
}
/*
* ppb_reset_epp_timeout()
*
* Reset the EPP timeout bit in the status register.
* Reset the EPP timeout bit in the status register
*/
int
ppb_reset_epp_timeout(struct ppb_device *dev)
@ -118,7 +138,7 @@ ppb_reset_epp_timeout(struct ppb_device *dev)
/*
* ppb_ecp_sync()
*
* Wait for the ECP FIFO to be empty.
* Wait for the ECP FIFO to be empty
*/
int
ppb_ecp_sync(struct ppb_device *dev)
@ -133,43 +153,10 @@ ppb_ecp_sync(struct ppb_device *dev)
return (0);
}
/*
* ppb_get_mode()
*
* Read the mode (SPP, EPP...) of the chipset.
*/
int
ppb_get_mode(struct ppb_device *dev)
{
return (dev->ppb->ppb_link->mode);
}
/*
* 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);
}
/*
* 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_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)

326
sys/dev/ppbus/ppb_msq.c Normal file
View file

@ -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);
}

190
sys/dev/ppbus/ppb_msq.h Normal file
View file

@ -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

View file

@ -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);
}
/*
@ -304,6 +311,24 @@ ppb_lookup_bus(int base_port)
return (ppb);
}
/*
* 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()
*
@ -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);

View file

@ -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) \
#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \
((dev)->ppb->ppb_link->adapter_unit)
#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \
#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \
((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_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \
((dev)->ppb->ppb_link->adapter_unit)
#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \
((dev)->ppb->ppb_link->adapter_unit)
#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \
#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \
((dev)->ppb->ppb_link->adapter_unit)
#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \
#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_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \
#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \
((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

View file

@ -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);

View file

@ -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 ++;
/* now, try to initialise the drive */
if (vpo_detect(vpo)) {
/* low level probe */
vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit);
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,18 +182,16 @@ 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;
vpo->sc_link.device = &vpo_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);
}

771
sys/dev/ppbus/vpoio.c Normal file
View file

@ -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);
}

View file

@ -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
#define VP0_SPP_WRITE_PULSE 253
#define VP0_NIBBLE_READ_PULSE 486
/*
* 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_sense {
struct scsi_sense cmd;
unsigned int stat;
unsigned int count;
struct vpo_nibble {
char h; /* most significant nibble */
char l; /* less significant nibble */
};
struct vpo_data {
unsigned short vpo_unit;
struct vpoio_data {
unsigned short int vpo_unit;
int vpo_stat;
int vpo_count;
int vpo_error;
struct vpo_nibble vpo_nibble;
struct ppb_status vpo_status;
struct vpo_sense vpo_sense;
unsigned char vpo_buffer[VP0_BUFFER_SIZE];
/* each device must have its own nibble inbyte microsequence */
struct ppb_microseq *vpo_nibble_inbyte_msq;
struct ppb_device vpo_dev;
struct scsi_link sc_link;
};
#define vpoio_set_unit(vpo,unit) ((vpo)->vpo_unit = unit)
struct ppb_device *vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo);
int vpoio_attach(struct vpoio_data *vpo);
int vpoio_detect(struct vpoio_data *vpo);
int vpoio_reset_bus(struct vpoio_data *vpo);
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

View file

@ -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;
ppc_reset_epp_timeout(ppc->ppc_unit);
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);
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);
if (PPB_IS_EPP(mode)) {
/*
* Try to reset EPP timeout bit.
* If it fails, try PS/2 and NIBBLE modes.
*/
ppc_reset_epp_timeout(ppc->ppc_unit);
r = r_str(ppc);
if (!(r & TIMEOUT))
return (mode);
} else {
if (mode)
return (mode);
}
/* detect PS/2 or NIBBLE mode */
return (ppc_detect_ps2(ppc));
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_check_ecpepp_timeout(struct ppc_data *ppc)
ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode)
{
char r;
int i, j, efer, base;
unsigned char r, hefere, hefras;
ppc_reset_epp_timeout(ppc->ppc_unit);
for (i = 0; i < 4; i ++) {
/* first try to enable configuration registers */
efer = w83877f_efers[i];
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_ECP_EPP);
/* 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;
/*
* 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
*/
if ((hefere | hefras) == w83877f_hefs[i])
goto found;
}
/* If EPP timeout bit is not reset, DON'T use EPP */
w_ecr(ppc, 0x20);
return (-1); /* failed */
return (PPB_ECP_PS2);
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;
}
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");
}
if (!chipset_mode) {
/* autodetect mode */
/* 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;
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 (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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;
/* don't know what to do here */
if (mode)
return (mode);
if (!chipset_mode) {
/* first, check for ECP */
w_ecr(ppc, 0x20);
if ((r_ecr(ppc) & 0xe0) == 0x20) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
/* try to reset EPP timeout bit */
ppc_reset_epp_timeout(ppc->ppc_unit);
/* search for SMC style ECP+EPP mode */
w_ecr(ppc, 0x80);
}
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_EPP);
}
/* try to reset EPP timeout bit */
if (ppc_check_epp_timeout(ppc)) {
ppc->ppc_avm |= PPB_EPP;
/* 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);
if (ppc->ppc_avm & PPB_ECP)
/* SMC like chipset found */
ppc->ppc_type = SMC_LIKE;
}
return (ppc_check_ecpepp_timeout(ppc));
}
/* XXX try to detect NIBBLE mode */
ppc->ppc_avm |= PPB_NIBBLE;
return (ppc_detect_ps2(ppc));
} else
ppc->ppc_avm = chipset_mode;
if (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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] : "");
/*

View file

@ -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,21 +51,19 @@ 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;
struct ppb_link ppc_link;
};
/*
* Parallel Port Chipset errors. XXX
*/
#define PPC_ENOPORT 9
/*
* Parallel Port Chipset registers.
*/
@ -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

View file

@ -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?

View file

@ -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?

View file

@ -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;
ppc_reset_epp_timeout(ppc->ppc_unit);
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);
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);
if (PPB_IS_EPP(mode)) {
/*
* Try to reset EPP timeout bit.
* If it fails, try PS/2 and NIBBLE modes.
*/
ppc_reset_epp_timeout(ppc->ppc_unit);
r = r_str(ppc);
if (!(r & TIMEOUT))
return (mode);
} else {
if (mode)
return (mode);
}
/* detect PS/2 or NIBBLE mode */
return (ppc_detect_ps2(ppc));
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_check_ecpepp_timeout(struct ppc_data *ppc)
ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode)
{
char r;
int i, j, efer, base;
unsigned char r, hefere, hefras;
ppc_reset_epp_timeout(ppc->ppc_unit);
for (i = 0; i < 4; i ++) {
/* first try to enable configuration registers */
efer = w83877f_efers[i];
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_ECP_EPP);
/* 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;
/*
* 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
*/
if ((hefere | hefras) == w83877f_hefs[i])
goto found;
}
/* If EPP timeout bit is not reset, DON'T use EPP */
w_ecr(ppc, 0x20);
return (-1); /* failed */
return (PPB_ECP_PS2);
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;
}
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");
}
if (!chipset_mode) {
/* autodetect mode */
/* 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;
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 (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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;
/* don't know what to do here */
if (mode)
return (mode);
if (!chipset_mode) {
/* first, check for ECP */
w_ecr(ppc, 0x20);
if ((r_ecr(ppc) & 0xe0) == 0x20) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
/* try to reset EPP timeout bit */
ppc_reset_epp_timeout(ppc->ppc_unit);
/* search for SMC style ECP+EPP mode */
w_ecr(ppc, 0x80);
}
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_EPP);
}
/* try to reset EPP timeout bit */
if (ppc_check_epp_timeout(ppc)) {
ppc->ppc_avm |= PPB_EPP;
/* 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);
if (ppc->ppc_avm & PPB_ECP)
/* SMC like chipset found */
ppc->ppc_type = SMC_LIKE;
}
return (ppc_check_ecpepp_timeout(ppc));
}
/* XXX try to detect NIBBLE mode */
ppc->ppc_avm |= PPB_NIBBLE;
return (ppc_detect_ps2(ppc));
} else
ppc->ppc_avm = chipset_mode;
if (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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] : "");
/*

View file

@ -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,21 +51,19 @@ 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;
struct ppb_link ppc_link;
};
/*
* Parallel Port Chipset errors. XXX
*/
#define PPC_ENOPORT 9
/*
* Parallel Port Chipset registers.
*/
@ -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

View file

@ -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;
ppc_reset_epp_timeout(ppc->ppc_unit);
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);
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);
if (PPB_IS_EPP(mode)) {
/*
* Try to reset EPP timeout bit.
* If it fails, try PS/2 and NIBBLE modes.
*/
ppc_reset_epp_timeout(ppc->ppc_unit);
r = r_str(ppc);
if (!(r & TIMEOUT))
return (mode);
} else {
if (mode)
return (mode);
}
/* detect PS/2 or NIBBLE mode */
return (ppc_detect_ps2(ppc));
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_check_ecpepp_timeout(struct ppc_data *ppc)
ppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode)
{
char r;
int i, j, efer, base;
unsigned char r, hefere, hefras;
ppc_reset_epp_timeout(ppc->ppc_unit);
for (i = 0; i < 4; i ++) {
/* first try to enable configuration registers */
efer = w83877f_efers[i];
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_ECP_EPP);
/* 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;
/*
* 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
*/
if ((hefere | hefras) == w83877f_hefs[i])
goto found;
}
/* If EPP timeout bit is not reset, DON'T use EPP */
w_ecr(ppc, 0x20);
return (-1); /* failed */
return (PPB_ECP_PS2);
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;
}
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");
}
if (!chipset_mode) {
/* autodetect mode */
/* 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;
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 (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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;
/* don't know what to do here */
if (mode)
return (mode);
if (!chipset_mode) {
/* first, check for ECP */
w_ecr(ppc, 0x20);
if ((r_ecr(ppc) & 0xe0) == 0x20) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
/* try to reset EPP timeout bit */
ppc_reset_epp_timeout(ppc->ppc_unit);
/* search for SMC style ECP+EPP mode */
w_ecr(ppc, 0x80);
}
r = r_str(ppc);
if (!(r & TIMEOUT)) {
return (PPB_EPP);
}
/* try to reset EPP timeout bit */
if (ppc_check_epp_timeout(ppc)) {
ppc->ppc_avm |= PPB_EPP;
/* 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);
if (ppc->ppc_avm & PPB_ECP)
/* SMC like chipset found */
ppc->ppc_type = SMC_LIKE;
}
return (ppc_check_ecpepp_timeout(ppc));
}
/* XXX try to detect NIBBLE mode */
ppc->ppc_avm |= PPB_NIBBLE;
return (ppc_detect_ps2(ppc));
} else
ppc->ppc_avm = chipset_mode;
if (ppc->ppc_avm & PPB_ECP)
ppc_ecp_config(ppc, chipset_mode);
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] : "");
/*

View file

@ -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,21 +51,19 @@ 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;
struct ppb_link ppc_link;
};
/*
* Parallel Port Chipset errors. XXX
*/
#define PPC_ENOPORT 9
/*
* Parallel Port Chipset registers.
*/
@ -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