summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authornsouch <nsouch@FreeBSD.org>1999-01-10 12:04:56 +0000
committernsouch <nsouch@FreeBSD.org>1999-01-10 12:04:56 +0000
commit88ae26fef69887203a871fca927f456a66c2cad4 (patch)
tree9e8c963e572bd5f141fcfc50127d8185cf16e87c /sys
parent44bde66cb074ad8bc5cdb36460c6ef9f4628ea69 (diff)
downloadFreeBSD-src-88ae26fef69887203a871fca927f456a66c2cad4.zip
FreeBSD-src-88ae26fef69887203a871fca927f456a66c2cad4.tar.gz
Major ppbus commit with:
+ ECP parallel port chipset FIFO detection + DMA+FIFO parallel I/O handled as chipset specific + nlpt updated in order to use the above enhanced parallel I/O. Use 'lptcontrol -e' to use enhanced I/O + Various options documented in LINT + Full IEEE1284 NIBBLE and BYTE modes support. See ppbus(4) for an overview of the IEEE1284 standard + Detection of PnP parallel devices at boot + Read capability added to nlpt driver to get IEEE1284 compliant printer status with a simple 'cat /dev/lpt0' + IEEE1284 peripheral emulation added to BYTE mode. Two computers may dialog according to IEEE1284 signaling method. See PERIPH_1284 option and /sys/dev/ppbus/ppi.c All this code is supposed to provide basic functions for IEEE1284 programming. ppi.c and nlpt.c may act as examples.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ppbus/immio.c4
-rw-r--r--sys/dev/ppbus/lpbb.c10
-rw-r--r--sys/dev/ppbus/nlpt.c125
-rw-r--r--sys/dev/ppbus/nlpt.h6
-rw-r--r--sys/dev/ppbus/ppb_1284.c770
-rw-r--r--sys/dev/ppbus/ppb_1284.h65
-rw-r--r--sys/dev/ppbus/ppb_base.c49
-rw-r--r--sys/dev/ppbus/ppb_msq.c22
-rw-r--r--sys/dev/ppbus/ppb_msq.h4
-rw-r--r--sys/dev/ppbus/ppbconf.c209
-rw-r--r--sys/dev/ppbus/ppbconf.h25
-rw-r--r--sys/dev/ppbus/ppi.c322
-rw-r--r--sys/dev/ppbus/vpo.c4
-rw-r--r--sys/dev/ppbus/vpoio.c19
-rw-r--r--sys/dev/ppc/ppc.c483
-rw-r--r--sys/dev/ppc/ppcreg.h55
-rw-r--r--sys/i386/isa/ppc.c483
-rw-r--r--sys/i386/isa/ppcreg.h55
-rw-r--r--sys/isa/ppc.c483
-rw-r--r--sys/isa/ppcreg.h55
20 files changed, 2880 insertions, 368 deletions
diff --git a/sys/dev/ppbus/immio.c b/sys/dev/ppbus/immio.c
index dc37ab3..67054f7 100644
--- a/sys/dev/ppbus/immio.c
+++ b/sys/dev/ppbus/immio.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: immio.c,v 1.3 1998/10/02 20:44:58 nsouch Exp $
+ * $Id: immio.c,v 1.4 1998/10/31 11:35:21 nsouch Exp $
*
*/
@@ -47,6 +47,8 @@
#include <sys/kernel.h>
#endif /*KERNEL */
+#include "opt_vpo.h"
+
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/ppb_msq.h>
#include <dev/ppbus/vpoio.h>
diff --git a/sys/dev/ppbus/lpbb.c b/sys/dev/ppbus/lpbb.c
index 7c77779..cd8ff24 100644
--- a/sys/dev/ppbus/lpbb.c
+++ b/sys/dev/ppbus/lpbb.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: lpbb.c,v 1.2 1998/11/09 22:22:01 nsouch Exp $
+ * $Id: lpbb.c,v 1.3 1998/12/07 21:58:16 archie Exp $
*
*/
@@ -240,14 +240,6 @@ else
return 0;
}
-static int getSCL(struct lpbb_softc *sc)
-{
-if((ppb_rstr(&sc->lpbb_dev)&SCL_in)==SCL_in)
- return 1;
-else
- return 0;
-}
-
static void setSDA(struct lpbb_softc *sc, char val)
{
if(val==0)
diff --git a/sys/dev/ppbus/nlpt.c b/sys/dev/ppbus/nlpt.c
index b39ee64..89d6c7f 100644
--- a/sys/dev/ppbus/nlpt.c
+++ b/sys/dev/ppbus/nlpt.c
@@ -47,7 +47,7 @@
*
* from: unknown origin, 386BSD 0.1
* From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
- * $Id: nlpt.c,v 1.10 1998/09/20 14:41:54 nsouch Exp $
+ * $Id: nlpt.c,v 1.11 1998/12/04 22:00:33 archie Exp $
*/
/*
@@ -81,8 +81,11 @@
#endif /*KERNEL*/
#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_1284.h>
#include <dev/ppbus/nlpt.h>
+#include "opt_nlpt.h"
+
#ifndef NLPT_DEBUG
#define nlprintf(args)
#else
@@ -99,6 +102,7 @@ static int volatile nlptflag = 1;
#define LPTOUTMAX 1 /* maximal timeout 1 s */
#define LPPRI (PZERO+8)
#define BUFSIZE 1024
+#define BUFSTATSIZE 32
#define LPTUNIT(s) ((s)&0x03)
#define LPTFLAGS(s) ((s)&0xfc)
@@ -163,11 +167,12 @@ DATA_SET(ppbdriver_set, nlptdriver);
static d_open_t nlptopen;
static d_close_t nlptclose;
static d_write_t nlptwrite;
+static d_read_t nlptread;
static d_ioctl_t nlptioctl;
#define CDEV_MAJOR 16
static struct cdevsw nlpt_cdevsw =
- { nlptopen, nlptclose, noread, nlptwrite, /*16*/
+ { nlptopen, nlptclose, nlptread, nlptwrite, /*16*/
nlptioctl, nullstop, nullreset, nodevtotty, /* lpt */
seltrue, nommap, nostrat, LPT_NAME, NULL, -1 };
@@ -176,6 +181,9 @@ lpt_request_ppbus(struct lpt_data *sc, int how)
{
int error;
+ if (sc->sc_state & HAVEBUS)
+ return (0);
+
/* we have the bus only if the request succeded */
if ((error = ppb_request_bus(&sc->lpt_dev, how)) == 0)
sc->sc_state |= HAVEBUS;
@@ -186,13 +194,10 @@ lpt_request_ppbus(struct lpt_data *sc, int how)
static int
lpt_release_ppbus(struct lpt_data *sc)
{
- int error;
-
- /* we do not have the bus only if the request succeeded */
- if ((error = ppb_release_bus(&sc->lpt_dev)) == 0)
- sc->sc_state &= ~HAVEBUS;
+ ppb_release_bus(&sc->lpt_dev);
+ sc->sc_state &= ~HAVEBUS;
- return (error);
+ return (0);
}
/*
@@ -439,7 +444,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
struct lpt_data *sc;
int s;
- int trys;
+ int trys, err;
u_int unit = LPTUNIT(minor(dev));
if ((unit >= nlpt))
@@ -462,9 +467,8 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
}
/* 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);
+ if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))
+ return (err);
s = spltty();
nlprintf((LPT_NAME " flags 0x%x\n", sc->sc_flags));
@@ -526,6 +530,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
sc->sc_state = OPEN;
sc->sc_inbuf = geteblk(BUFSIZE);
+ sc->sc_statbuf = geteblk(BUFSTATSIZE);
sc->sc_xfercnt = 0;
splx(s);
@@ -559,8 +564,7 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p)
if(sc->sc_flags & LP_BYPASS)
goto end_close;
- if ((sc->sc_state & HAVEBUS) == 0 &&
- (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)))
+ if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))
return (err);
sc->sc_state &= ~OPEN;
@@ -577,6 +581,7 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p)
ppb_wctr(&sc->lpt_dev, LPC_NINIT);
brelse(sc->sc_inbuf);
+ brelse(sc->sc_statbuf);
end_close:
/* release the bus anyway */
@@ -649,6 +654,40 @@ nlpt_pushbytes(struct lpt_data *sc)
}
/*
+ * nlptread --retrieve printer status in IEEE1284 NIBBLE mode
+ */
+
+static int
+nlptread(dev_t dev, struct uio *uio, int ioflag)
+{
+ struct lpt_data *sc = lptdata[LPTUNIT(minor(dev))];
+ int error = 0, len;
+
+ if ((error = ppb_1284_negociate(&sc->lpt_dev, PPB_NIBBLE, 0)))
+ return (error);
+
+ /* read data in an other buffer, read/write may be simultaneous */
+ len = 0;
+ while (uio->uio_resid) {
+ if ((error = ppb_1284_read(&sc->lpt_dev, PPB_NIBBLE,
+ sc->sc_statbuf->b_data, min(BUFSTATSIZE,
+ uio->uio_resid), &len))) {
+ goto error;
+ }
+
+ if (!len)
+ goto error; /* no more data */
+
+ if ((error = uiomove(sc->sc_statbuf->b_data, len, uio)))
+ goto error;
+ }
+
+error:
+ ppb_1284_terminate(&sc->lpt_dev);
+ return (error);
+}
+
+/*
* nlptwrite --copy a line from user space to a local buffer, then call
* putc to get the chars moved to the output queue.
*
@@ -660,6 +699,7 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag)
{
register unsigned n;
int pl, err;
+ u_int unit = LPTUNIT(minor(dev));
struct lpt_data *sc = lptdata[LPTUNIT(minor(dev))];
if(sc->sc_flags & LP_BYPASS) {
@@ -668,16 +708,36 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag)
}
/* 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);
+ if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))
+ return (err);
sc->sc_state &= ~INTERRUPTED;
while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {
sc->sc_cp = sc->sc_inbuf->b_data ;
uiomove(sc->sc_cp, n, uio);
sc->sc_xfercnt = n ;
- while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
+
+ if (sc->sc_irq & LP_ENABLE_EXT) {
+ /* try any extended mode */
+ err = ppb_write(&sc->lpt_dev, sc->sc_cp,
+ sc->sc_xfercnt, 0);
+ switch (err) {
+ case 0:
+ /* if not all data was sent, we could rely
+ * on polling for the last bytes */
+ sc->sc_xfercnt = 0;
+ break;
+ case EINTR:
+ sc->sc_state |= INTERRUPTED;
+ return(err);
+ case EINVAL:
+ /* advanced mode not avail */
+ log(LOG_NOTICE, LPT_NAME "%d: advanced mode not avail, polling\n", unit);
+ break;
+ default:
+ return(err);
+ }
+ } else while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
nlprintf(("i"));
/* if the printer is ready for a char, */
/* give it one */
@@ -695,6 +755,7 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag)
return(err);
}
}
+
/* check to see if we must do a polled write */
if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {
nlprintf(("p"));
@@ -809,15 +870,35 @@ nlptioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
* this gets syslog'd.
*/
old_sc_irq = sc->sc_irq;
- if(*(int*)data == 0)
+ switch(*(int*)data) {
+ case 0:
sc->sc_irq &= (~LP_ENABLE_IRQ);
- else
+ break;
+ case 1:
+ sc->sc_irq &= (~LP_ENABLE_EXT);
sc->sc_irq |= LP_ENABLE_IRQ;
+ break;
+ case 2:
+ /* classic irq based transfer and advanced
+ * modes are in conflict
+ */
+ sc->sc_irq &= (~LP_ENABLE_IRQ);
+ sc->sc_irq |= LP_ENABLE_EXT;
+ break;
+ case 3:
+ sc->sc_irq &= (~LP_ENABLE_EXT);
+ break;
+ default:
+ break;
+ }
+
if (old_sc_irq != sc->sc_irq )
- log(LOG_NOTICE, LPT_NAME "%d: switched to %s mode\n",
+ log(LOG_NOTICE, LPT_NAME "%d: switched to %s %s mode\n",
unit,
(sc->sc_irq & LP_ENABLE_IRQ)?
- "interrupt-driven":"polled");
+ "interrupt-driven":"polled",
+ (sc->sc_irq & LP_ENABLE_EXT)?
+ "extended":"standard");
} else /* polled port */
error = EOPNOTSUPP;
break;
diff --git a/sys/dev/ppbus/nlpt.h b/sys/dev/ppbus/nlpt.h
index b0b3df6..31292b9 100644
--- a/sys/dev/ppbus/nlpt.h
+++ b/sys/dev/ppbus/nlpt.h
@@ -27,7 +27,7 @@
* @(#)lptreg.h 1.1 (Berkeley) 12/19/90
* Id: lptreg.h,v 1.6 1997/02/22 09:36:52 peter Exp
*
- * $Id: nlpt.h,v 1.1 1997/08/14 13:57:40 msmith Exp $
+ * $Id: nlpt.h,v 1.2 1997/08/16 14:05:32 msmith Exp $
*/
#ifndef __NLPT_H
#define __NLPT_H
@@ -61,13 +61,15 @@ struct lpt_data {
#define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */
#define LP_BYPASS 0x80 /* bypass printer ready checks */
struct buf *sc_inbuf;
+ struct buf *sc_statbuf;
short sc_xfercnt ;
char sc_primed;
char *sc_cp ;
- u_char sc_irq ; /* IRQ status of port */
+ u_short sc_irq ; /* IRQ status of port */
#define LP_HAS_IRQ 0x01 /* we have an irq available */
#define LP_USE_IRQ 0x02 /* we are using our irq */
#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
+#define LP_ENABLE_EXT 0x10 /* we shall use advanced mode when possible */
u_char sc_backoff ; /* time to call lptout() again */
#ifdef DEVFS
diff --git a/sys/dev/ppbus/ppb_1284.c b/sys/dev/ppbus/ppb_1284.c
index 56524d1..96cd6f2 100644
--- a/sys/dev/ppbus/ppb_1284.c
+++ b/sys/dev/ppbus/ppb_1284.c
@@ -23,11 +23,15 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_1284.c,v 1.5 1998/09/13 18:26:26 nsouch Exp $
+ * $Id: ppb_1284.c,v 1.6 1998/09/13 20:44:55 nsouch Exp $
*
*/
-#include "opt_debug_1284.h"
+/*
+ * General purpose routines for the IEEE1284-1994 Standard
+ */
+
+#include "opt_ppb_1284.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -42,24 +46,414 @@
*
* Wait for the peripherial up to 40ms
*/
-int
+static int
do_1284_wait(struct ppb_device *dev, char mask, char status)
{
- int i;
+ return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR | PPB_POLL));
+}
+
+static int
+do_peripheral_wait(struct ppb_device *dev, char mask, char status)
+{
+ return (ppb_poll_device(dev, 100, mask, status, PPB_NOINTR | PPB_POLL));
+}
+
+#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4)
+
+/*
+ * ppb_1284_reset_error()
+ *
+ * Unconditionaly reset the error field
+ */
+static int
+ppb_1284_reset_error(struct ppb_device *dev, int state)
+{
+ dev->ppb->error = PPB_NO_ERROR;
+ dev->ppb->state = state;
+
+ return (0);
+}
+
+/*
+ * ppb_1284_get_state()
+ *
+ * Get IEEE1284 state
+ */
+static int
+ppb_1284_get_state(struct ppb_device *dev)
+{
+ return (dev->ppb->state);
+}
+
+/*
+ * ppb_1284_set_state()
+ *
+ * Change IEEE1284 state if no error occured
+ */
+static int
+ppb_1284_set_state(struct ppb_device *dev, int state)
+{
+ /* call ppb_1284_reset_error() if you absolutly want to change
+ * the state from PPB_ERROR to another */
+ if ((dev->ppb->state != PPB_ERROR) &&
+ (dev->ppb->error == PPB_NO_ERROR)) {
+ dev->ppb->state = state;
+ dev->ppb->error = PPB_NO_ERROR;
+ }
+
+ return (0);
+}
+
+static int
+ppb_1284_set_error(struct ppb_device *dev, int error, int event)
+{
+ /* do not accumulate errors */
+ if ((dev->ppb->error == PPB_NO_ERROR) &&
+ (dev->ppb->state != PPB_ERROR)) {
+ dev->ppb->error = error;
+ dev->ppb->state = PPB_ERROR;
+ }
+
+#ifdef DEBUG_1284
+ printf("ppb1284: error=%d status=0x%x event=%d\n", error,
+ ppb_rstr(dev) & 0xff, event);
+#endif
+
+ return (0);
+}
+
+/*
+ * ppb_request_mode()
+ *
+ * Converts mode+options into ext. value
+ */
+static int
+ppb_request_mode(int mode, int options)
+{
+ int request_mode = 0;
+
+ if (options & PPB_EXTENSIBILITY_LINK) {
+ request_mode = EXT_LINK_1284_NORMAL;
+
+ } else {
+ switch (mode) {
+ case PPB_NIBBLE:
+ request_mode = (options & PPB_REQUEST_ID) ?
+ NIBBLE_1284_REQUEST_ID :
+ NIBBLE_1284_NORMAL;
+ break;
+ case PPB_PS2:
+ request_mode = (options & PPB_REQUEST_ID) ?
+ BYTE_1284_REQUEST_ID :
+ BYTE_1284_NORMAL;
+ break;
+ case PPB_ECP:
+ if (options & PPB_USE_RLE)
+ request_mode = (options & PPB_REQUEST_ID) ?
+ ECP_1284_RLE_REQUEST_ID :
+ ECP_1284_RLE;
+ else
+ request_mode = (options & PPB_REQUEST_ID) ?
+ ECP_1284_REQUEST_ID :
+ ECP_1284_NORMAL;
+ break;
+ case PPB_EPP:
+ request_mode = EPP_1284_NORMAL;
+ break;
+ default:
+ panic("%s: unsupported mode %d\n", __FUNCTION__, mode);
+ }
+ }
+
+ return (request_mode);
+}
+
+/*
+ * ppb_peripheral_negociate()
+ *
+ * Negociate the peripheral side
+ */
+int
+ppb_peripheral_negociate(struct ppb_device *dev, int mode, int options)
+{
+ int spin, request_mode, error = 0;
char r;
- /* try up to 5ms */
- for (i = 0; i < 20; i++) {
- r = ppb_rstr(dev);
- DELAY(25);
- if ((r & mask) == status)
- return (0);
+ ppb_set_mode(dev, PPB_COMPATIBLE);
+ ppb_1284_set_state(dev, PPB_PERIPHERAL_NEGOCIATION);
+
+ /* compute ext. value */
+ request_mode = ppb_request_mode(mode, options);
+
+ /* wait host */
+ spin = 10;
+ while (spin-- && (ppb_rstr(dev) & nBUSY))
+ DELAY(1);
+
+ /* check termination */
+ if (!(ppb_rstr(dev) & SELECT) || !spin) {
+ error = ENODEV;
+ goto error;
+ }
+
+ /* Event 4 - read ext. value */
+ r = ppb_rdtr(dev);
+
+ /* nibble mode is not supported */
+ if ((r == (char)request_mode) ||
+ (r == NIBBLE_1284_NORMAL)) {
+
+ /* Event 5 - restore direction bit, no data avail */
+ ppb_wctr(dev, (STROBE | nINIT) & ~(SELECTIN));
+ DELAY(1);
+
+ /* Event 6 */
+ ppb_wctr(dev, (nINIT) & ~(SELECTIN | STROBE));
+
+ if (r == NIBBLE_1284_NORMAL) {
+#ifdef DEBUG_1284
+ printf("R");
+#endif
+ ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 4);
+ error = EINVAL;
+ goto error;
+ } else {
+ ppb_1284_set_state(dev, PPB_PERIPHERAL_IDLE);
+ switch (r) {
+ case BYTE_1284_NORMAL:
+ ppb_set_mode(dev, PPB_BYTE);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_1284
+ printf("A");
+#endif
+ /* negociation succeeds */
+ }
+ } else {
+ /* Event 5 - mode not supported */
+ ppb_wctr(dev, SELECTIN);
+ DELAY(1);
+
+ /* Event 6 */
+ ppb_wctr(dev, (SELECTIN) & ~(STROBE | nINIT));
+ ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 4);
+
+#ifdef DEBUG_1284
+ printf("r");
+#endif
+ error = EINVAL;
+ goto error;
}
- return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR));
+ return (0);
+
+error:
+ ppb_peripheral_terminate(dev, PPB_WAIT);
+ return (error);
}
-#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4)
+/*
+ * ppb_peripheral_terminate()
+ *
+ * Terminate peripheral transfer side
+ *
+ * Always return 0 in compatible mode
+ */
+int
+ppb_peripheral_terminate(struct ppb_device *dev, int how)
+{
+ int error = 0;
+
+#ifdef DEBUG_1284
+ printf("t");
+#endif
+
+ ppb_1284_set_state(dev, PPB_PERIPHERAL_TERMINATION);
+
+ /* Event 22 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(dev, SELECT | nBUSY, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 22);
+ goto error;
+ }
+
+ /* Event 24 */
+ ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+
+ /* Event 25 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(dev, nBUSY, nBUSY))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 25);
+ goto error;
+ }
+
+ /* Event 26 */
+ ppb_wctr(dev, (SELECTIN | nINIT | STROBE) & ~(AUTOFEED));
+ DELAY(1);
+ /* Event 27 */
+ ppb_wctr(dev, (SELECTIN | nINIT) & ~(STROBE | AUTOFEED));
+
+ /* Event 28 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(dev, nBUSY, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 28);
+ goto error;
+ }
+
+error:
+ ppb_set_mode(dev, PPB_COMPATIBLE);
+ ppb_1284_set_state(dev, PPB_FORWARD_IDLE);
+
+ return (0);
+}
+
+/*
+ * byte_peripheral_outbyte()
+ *
+ * Write 1 byte in BYTE mode
+ */
+static int
+byte_peripheral_outbyte(struct ppb_device *dev, char *buffer, int last)
+{
+ int error = 0;
+
+ /* Event 7 */
+ if ((error = do_1284_wait(dev, nBUSY, nBUSY))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 7);
+ goto error;
+ }
+
+ /* check termination */
+ if (!(ppb_rstr(dev) & SELECT)) {
+ ppb_peripheral_terminate(dev, PPB_WAIT);
+ goto error;
+ }
+
+ /* Event 15 - put byte on data lines */
+#ifdef DEBUG_1284
+ printf("B");
+#endif
+ ppb_wdtr(dev, *buffer);
+
+ /* Event 9 */
+ ppb_wctr(dev, (AUTOFEED | STROBE) & ~(nINIT | SELECTIN));
+
+ /* Event 10 - wait data read */
+ if ((error = do_peripheral_wait(dev, nBUSY, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 16);
+ goto error;
+ }
+
+ /* Event 11 */
+ if (!last) {
+ ppb_wctr(dev, (AUTOFEED) & ~(nINIT | STROBE | SELECTIN));
+ } else {
+ ppb_wctr(dev, (nINIT) & ~(STROBE | SELECTIN | AUTOFEED));
+ }
+
+#if 0
+ /* Event 16 - wait strobe */
+ if ((error = do_peripheral_wait(dev, nACK | nBUSY, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 16);
+ goto error;
+ }
+#endif
+
+ /* check termination */
+ if (!(ppb_rstr(dev) & SELECT)) {
+ ppb_peripheral_terminate(dev, PPB_WAIT);
+ goto error;
+ }
+
+error:
+ return (error);
+}
+
+/*
+ * byte_peripheral_write()
+ *
+ * Write n bytes in BYTE mode
+ */
+int
+byte_peripheral_write(struct ppb_device *dev, char *buffer, int len, int *sent)
+{
+ int error = 0, i;
+ char r;
+
+ ppb_1284_set_state(dev, PPB_PERIPHERAL_TRANSFER);
+
+ /* wait forever, the remote host is master and should initiate
+ * termination
+ */
+ for (i=0; i<len; i++) {
+ /* force remote nFAULT low to release the remote waiting
+ * process, if any
+ */
+ r = ppb_rctr(dev);
+ ppb_wctr(dev, r & ~nINIT);
+
+#ifdef DEBUG_1284
+ printf("y");
+#endif
+ /* Event 7 */
+ error = ppb_poll_device(dev, PPB_FOREVER, nBUSY, nBUSY,
+ PPB_INTR);
+
+ if (error && error != EWOULDBLOCK)
+ goto error;
+
+#ifdef DEBUG_1284
+ printf("b");
+#endif
+ if ((error = byte_peripheral_outbyte(dev, buffer+i, (i == len-1))))
+ goto error;
+ }
+error:
+ if (!error)
+ ppb_1284_set_state(dev, PPB_PERIPHERAL_IDLE);
+
+ *sent = i;
+ return (error);
+}
+
+/*
+ * byte_1284_inbyte()
+ *
+ * Read 1 byte in BYTE mode
+ */
+int
+byte_1284_inbyte(struct ppb_device *dev, char *buffer)
+{
+ int error = 0;
+
+ /* Event 7 - ready to take data (nAUTO low) */
+ ppb_wctr(dev, (PCD | nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
+
+ /* Event 9 - peripheral set nAck low */
+ if ((error = do_1284_wait(dev, nACK, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 9);
+ goto error;
+ }
+
+ /* read the byte */
+ *buffer = ppb_rdtr(dev);
+
+ /* Event 10 - data received, can't accept more */
+ ppb_wctr(dev, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN));
+
+ /* Event 11 - peripheral ack */
+ if ((error = do_1284_wait(dev, nACK, nACK))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 11);
+ goto error;
+ }
+
+ /* Event 16 - strobe */
+ ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+ DELAY(3);
+ ppb_wctr(dev, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN));
+
+error:
+ return (error);
+}
/*
* nibble_1284_inbyte()
@@ -73,126 +467,372 @@ nibble_1284_inbyte(struct ppb_device *dev, char *buffer)
int i, error;
for (i = 0; i < 2; i++) {
- /* ready to take data (nAUTO low) */
- ppb_wctr(dev, AUTOFEED & ~(STROBE | SELECTIN));
- if ((error = do_1284_wait(dev, nACK, 0)))
- return (error);
+ /* Event 7 - ready to take data (nAUTO low) */
+ ppb_wctr(dev, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
+
+ /* Event 8 - peripheral writes the first nibble */
+
+ /* Event 9 - peripheral set nAck low */
+ if ((error = do_1284_wait(dev, nACK, 0))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 9);
+ goto error;
+ }
/* read nibble */
nibble[i] = ppb_rstr(dev);
- /* ack, not ready for another nibble */
- ppb_wctr(dev, 0 & ~(AUTOFEED | STROBE | SELECTIN));
+ /* Event 10 - ack, nibble received */
+ ppb_wctr(dev, nINIT & ~(AUTOFEED | STROBE | SELECTIN));
- /* wait ack from peripherial */
- if ((error = do_1284_wait(dev, nACK, nACK)))
- return (error);
+ /* Event 11 - wait ack from peripherial */
+ if ((error = do_1284_wait(dev, nACK, nACK))) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 11);
+ goto error;
+ }
}
*buffer = ((nibble2char(nibble[1]) << 4) & 0xf0) |
(nibble2char(nibble[0]) & 0x0f);
- return (0);
+error:
+ return (error);
+}
+
+/*
+ * spp_1284_read()
+ *
+ * Read in IEEE1284 NIBBLE/BYTE mode
+ */
+int
+spp_1284_read(struct ppb_device *dev, int mode, char *buffer, int max, int *read)
+{
+ int error = 0, len = 0;
+ int terminate_after_transfer = 1;
+ int state;
+
+ *read = len = 0;
+
+ state = ppb_1284_get_state(dev);
+
+ switch (state) {
+ case PPB_FORWARD_IDLE:
+ if ((error = ppb_1284_negociate(dev, mode, 0)))
+ return (error);
+ break;
+
+ case PPB_REVERSE_IDLE:
+ terminate_after_transfer = 0;
+ break;
+
+ default:
+ ppb_1284_terminate(dev);
+ if ((error = ppb_1284_negociate(dev, mode, 0)))
+ return (error);
+ break;
+ }
+
+ while ((len < max) && !(ppb_rstr(dev) & (nFAULT))) {
+
+ ppb_1284_set_state(dev, PPB_REVERSE_TRANSFER);
+
+#ifdef DEBUG_1284
+ printf("B");
+#endif
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ /* read a byte, error means no more data */
+ if (nibble_1284_inbyte(dev, buffer+len))
+ goto end_while;
+ break;
+ case PPB_BYTE:
+ if (byte_1284_inbyte(dev, buffer+len))
+ goto end_while;
+ break;
+ default:
+ error = EINVAL;
+ goto end_while;
+ }
+ len ++;
+ }
+end_while:
+
+ if (!error)
+ ppb_1284_set_state(dev, PPB_REVERSE_IDLE);
+
+ *read = len;
+
+ if (terminate_after_transfer || error)
+ ppb_1284_terminate(dev);
+
+ return (error);
}
/*
- * nibble_1284_sync()
+ * ppb_1284_read_id()
+ *
*/
-void
-nibble_1284_sync(struct ppb_device *dev)
+int
+ppb_1284_read_id(struct ppb_device *dev, int mode, char *buffer,
+ int max, int *read)
{
- char ctr;
+ int error = 0;
- ctr = ppb_rctr(dev);
+ /* fill the buffer with 0s */
+ bzero(buffer, max);
- ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN);
- if (do_1284_wait(dev, nACK, 0))
- return;
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_ECP:
+ if ((error = ppb_1284_negociate(dev, PPB_NIBBLE, PPB_REQUEST_ID)))
+ return (error);
+ error = spp_1284_read(dev, PPB_NIBBLE, buffer, max, read);
+ break;
+ case PPB_BYTE:
+ if ((error = ppb_1284_negociate(dev, PPB_BYTE, PPB_REQUEST_ID)))
+ return (error);
+ error = spp_1284_read(dev, PPB_BYTE, buffer, max, read);
+ break;
+ default:
+ panic("%s: unsupported mode %d\n", __FUNCTION__, mode);
+ }
- ppb_wctr(dev, ctr | AUTOFEED);
- do_1284_wait(dev, nACK, nACK);
+ ppb_1284_terminate(dev);
+ return (error);
+}
- ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN);
+/*
+ * ppb_1284_read()
+ *
+ * IEEE1284 read
+ */
+int
+ppb_1284_read(struct ppb_device *dev, int mode, char *buffer,
+ int max, int *read)
+{
+ int error = 0;
- return;
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_BYTE:
+ error = spp_1284_read(dev, mode, buffer, max, read);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (error);
}
/*
* ppb_1284_negociate()
*
+ * IEEE1284 negociation phase
+ *
* Normal nibble mode or request device id mode (see ppb_1284.h)
+ *
+ * After negociation, nFAULT is low if data is available
*/
int
-ppb_1284_negociate(struct ppb_device *dev, int mode)
+ppb_1284_negociate(struct ppb_device *dev, int mode, int options)
{
int error;
- int phase = 0;
+ int request_mode;
+
+#ifdef DEBUG_1284
+ printf("n");
+#endif
+
+ if (ppb_1284_get_state(dev) >= PPB_PERIPHERAL_NEGOCIATION)
+ ppb_peripheral_terminate(dev, PPB_WAIT);
+ if (ppb_1284_get_state(dev) != PPB_FORWARD_IDLE)
+ ppb_1284_terminate(dev);
+
+#ifdef DEBUG_1284
+ printf("%d", mode);
+#endif
+
+ /* ensure the host is in compatible mode */
+ ppb_set_mode(dev, PPB_COMPATIBLE);
+
+ /* reset error to catch the actual negociation error */
+ ppb_1284_reset_error(dev, PPB_FORWARD_IDLE);
+
+ /* calculate ext. value */
+ request_mode = ppb_request_mode(mode, options);
+
+ /* default state */
ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
DELAY(1);
- ppb_wdtr(dev, mode);
+ /* enter negociation phase */
+ ppb_1284_set_state(dev, PPB_NEGOCIATION);
+
+ /* Event 0 - put the exten. value on the data lines */
+ ppb_wdtr(dev, request_mode);
+
+#ifdef PERIPH_1284
+ /* request remote host attention */
+ ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+ DELAY(1);
+ ppb_wctr(dev, (nINIT) & ~(STROBE | AUTOFEED | SELECTIN));
+#else
DELAY(1);
+#endif /* !PERIPH_1284 */
+
+ /* Event 1 - enter IEEE1284 mode */
ppb_wctr(dev, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
- if ((error = do_1284_wait(dev, nACK | PERROR | SELECT | nFAULT,
- PERROR | SELECT | nFAULT)))
+#ifdef PERIPH_1284
+ /* ignore the PError line, wait a bit more, remote host's
+ * interrupts don't respond fast enough */
+ if (ppb_poll_device(dev, 40, nACK | SELECT | nFAULT,
+ SELECT | nFAULT, PPB_NOINTR | PPB_POLL)) {
+ ppb_1284_set_error(dev, PPB_NOT_IEEE1284, 2);
+ error = ENODEV;
+ goto error;
+ }
+#else
+ /* Event 2 - trying IEEE1284 dialog */
+ if (do_1284_wait(dev, nACK | PERROR | SELECT | nFAULT,
+ PERROR | SELECT | nFAULT)) {
+ ppb_1284_set_error(dev, PPB_NOT_IEEE1284, 2);
+ error = ENODEV;
goto error;
+ }
+#endif /* !PERIPH_1284 */
- phase = 1;
-
+ /* Event 3 - latch the ext. value to the peripheral */
ppb_wctr(dev, (nINIT | STROBE | AUTOFEED) & ~SELECTIN);
- DELAY(5);
+ DELAY(1);
+ /* Event 4 - IEEE1284 device recognized */
ppb_wctr(dev, nINIT & ~(SELECTIN | AUTOFEED | STROBE));
-#if 0 /* not respected by most devices */
- if ((error = do_1284_wait(dev, nACK, nACK)))
+ /* Event 6 - waiting for status lines */
+ if (do_1284_wait(dev, nACK, nACK)) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 6);
+ error = EBUSY;
goto error;
+ }
- if (mode == 0)
- if ((error = do_1284_wait(dev, SELECT, 0)))
+ /* Event 7 - quering result consider nACK not to misunderstand
+ * a remote computer terminate sequence */
+ if (request_mode == NIBBLE_1284_NORMAL) {
+ if (do_1284_wait(dev, nACK | SELECT, nACK)) {
+ ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 7);
+ error = ENODEV;
goto error;
- else
- if ((error = do_1284_wait(dev, SELECT, SELECT)))
+ }
+ } else {
+ if (do_1284_wait(dev, nACK | SELECT, SELECT | nACK)) {
+ ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 7);
+ error = ENODEV;
goto error;
-#endif
+ }
+ }
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_PS2:
+ /* enter reverse idle phase */
+ ppb_1284_set_state(dev, PPB_REVERSE_IDLE);
+ break;
+ case PPB_ECP:
+ /* negociation ok, now setup the communication */
+ ppb_1284_set_state(dev, PPB_SETUP);
+ ppb_wctr(dev, (nINIT | AUTOFEED) & ~(SELECTIN | STROBE));
+
+#ifdef PERIPH_1284
+ /* ignore PError line */
+ if (do_1284_wait(dev, nACK | SELECT | nBUSY,
+ nACK | SELECT | nBUSY)) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 30);
+ error = ENODEV;
+ goto error;
+ }
+#else
+ if (do_1284_wait(dev, nACK | SELECT | PERROR | nBUSY,
+ nACK | SELECT | PERROR | nBUSY)) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 30);
+ error = ENODEV;
+ goto error;
+ }
+#endif /* !PERIPH_1284 */
+
+ /* ok, the host enters the ForwardIdle state */
+ ppb_1284_set_state(dev, PPB_ECP_FORWARD_IDLE);
+ break;
+ case PPB_EPP:
+ ppb_1284_set_state(dev, PPB_EPP_IDLE);
+ break;
+
+ default:
+ panic("%s: unknown mode (%d)!", __FUNCTION__, mode);
+ }
+ ppb_set_mode(dev, mode);
return (0);
error:
- if (bootverbose)
- printf("%s: status=0x%x %d\n", __FUNCTION__, ppb_rstr(dev), phase);
+ ppb_1284_terminate(dev);
return (error);
}
+/*
+ * ppb_1284_terminate()
+ *
+ * IEEE1284 termination phase, return code should ignored since the host
+ * is _always_ in compatible mode after ppb_1284_terminate()
+ */
int
-ppb_1284_terminate(struct ppb_device *dev, int how)
+ppb_1284_terminate(struct ppb_device *dev)
{
- int error;
- switch (how) {
- case VALID_STATE:
+#ifdef DEBUG_1284
+ printf("T");
+#endif
- ppb_wctr(dev, SELECTIN & ~(STROBE | AUTOFEED));
+ /* do not reset error here to keep the error that
+ * may occured before the ppb_1284_terminate() call */
+ ppb_1284_set_state(dev, PPB_TERMINATION);
- if ((error = do_1284_wait(dev, nACK | nBUSY | nFAULT, nFAULT)))
- return (error);
+#ifdef PERIPH_1284
+ /* request remote host attention */
+ ppb_wctr(dev, (nINIT | STROBE | SELECTIN) & ~(AUTOFEED));
+ DELAY(1);
+#endif /* PERIPH_1284 */
- ppb_wctr(dev, (SELECTIN | AUTOFEED) & ~STROBE);
+ /* Event 22 - set nSelectin low and nAutoFeed high */
+ ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
- if ((error = do_1284_wait(dev, nACK, nACK)))
- return (error);
+ /* Event 24 - waiting for peripheral, Xflag ignored */
+ if (do_1284_wait(dev, nACK | nBUSY | nFAULT, nFAULT)) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 24);
+ goto error;
+ }
- ppb_wctr(dev, SELECTIN & ~(STROBE | AUTOFEED));
- break;
+ /* Event 25 - set nAutoFd low */
+ ppb_wctr(dev, (nINIT | SELECTIN | AUTOFEED) & ~STROBE);
- default:
- return (EINVAL);
+ /* Event 26 - compatible mode status is set */
+
+ /* Event 27 - peripheral set nAck high */
+ if (do_1284_wait(dev, nACK, nACK)) {
+ ppb_1284_set_error(dev, PPB_TIMEOUT, 27);
}
+ /* Event 28 - end termination, return to idle phase */
+ ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
+
+error:
+ /* return to compatible mode */
+ ppb_set_mode(dev, PPB_COMPATIBLE);
+ ppb_1284_set_state(dev, PPB_FORWARD_IDLE);
+
return (0);
}
diff --git a/sys/dev/ppbus/ppb_1284.h b/sys/dev/ppbus/ppb_1284.h
index 1cc98d4..ea009b7d 100644
--- a/sys/dev/ppbus/ppb_1284.h
+++ b/sys/dev/ppbus/ppb_1284.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_1284.h,v 1.2 1998/08/03 19:14:31 msmith Exp $
+ * $Id: ppb_1284.h,v 1.3 1998/09/13 18:26:26 nsouch Exp $
*
*/
#ifndef __1284_H
@@ -66,20 +66,59 @@
#define Intr nACK
/* request mode values */
-#define NIBBLE_1284_NORMAL 0
-#define NIBBLE_1284_REQUEST_ID 4
-
-/* how to terminate */
-#define VALID_STATE 0
-#define IMMEDIATE 1
-
-extern int do_1284_wait(struct ppb_device *, char, char);
+#define NIBBLE_1284_NORMAL 0x0
+#define NIBBLE_1284_REQUEST_ID 0x4
+#define BYTE_1284_NORMAL 0x1
+#define BYTE_1284_REQUEST_ID 0x5
+#define ECP_1284_NORMAL 0x10
+#define ECP_1284_REQUEST_ID 0x14
+#define ECP_1284_RLE 0x30
+#define ECP_1284_RLE_REQUEST_ID 0x34
+#define EPP_1284_NORMAL 0x40
+#define EXT_LINK_1284_NORMAL 0x80
+
+/* ieee1284 mode options */
+#define PPB_REQUEST_ID 0x1
+#define PPB_USE_RLE 0x2
+#define PPB_EXTENSIBILITY_LINK 0x4
+
+/* ieee1284 errors */
+#define PPB_NO_ERROR 0
+#define PPB_MODE_UNSUPPORTED 1 /* mode not supported by peripheral */
+#define PPB_NOT_IEEE1284 2 /* not an IEEE1284 compliant periph. */
+#define PPB_TIMEOUT 3 /* timeout */
+#define PPB_INVALID_MODE 4 /* current mode is incorrect */
+
+/* ieee1284 host side states */
+#define PPB_ERROR 0
+#define PPB_FORWARD_IDLE 1
+#define PPB_NEGOCIATION 2
+#define PPB_SETUP 3
+#define PPB_ECP_FORWARD_IDLE 4
+#define PPB_FWD_TO_REVERSE 5
+#define PPB_REVERSE_IDLE 6
+#define PPB_REVERSE_TRANSFER 7
+#define PPB_REVERSE_TO_FWD 8
+#define PPB_EPP_IDLE 9
+#define PPB_TERMINATION 10
+
+/* peripheral side states */
+#define PPB_PERIPHERAL_NEGOCIATION 11
+#define PPB_PERIPHERAL_IDLE 12
+#define PPB_PERIPHERAL_TRANSFER 13
+#define PPB_PERIPHERAL_TERMINATION 14
extern int nibble_1284_inbyte(struct ppb_device *, char *);
-extern void nibble_1284_sync(struct ppb_device *);
-extern int nibble_1284_mode(struct ppb_device *, int);
+extern int byte_1284_inbyte(struct ppb_device *, char *);
+extern int spp_1284_read(struct ppb_device *, int, char *, int, int *);
+
+extern int ppb_1284_negociate(struct ppb_device *, int, int);
+extern int ppb_1284_terminate(struct ppb_device *);
+extern int ppb_1284_read_id(struct ppb_device *, int, char *, int, int *);
+extern int ppb_1284_read(struct ppb_device *, int, char *, int, int *);
-extern int ppb_1284_negociate(struct ppb_device *, int);
-extern int ppb_1284_terminate(struct ppb_device *, int how);
+extern int ppb_peripheral_terminate(struct ppb_device *, int);
+extern int ppb_peripheral_negociate(struct ppb_device *, int, int);
+extern int byte_peripheral_write(struct ppb_device *, char *, int, int *);
#endif
diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c
index 7555b24..83fc26e 100644
--- a/sys/dev/ppbus/ppb_base.c
+++ b/sys/dev/ppbus/ppb_base.c
@@ -23,12 +23,13 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_base.c,v 1.4 1998/08/03 19:14:31 msmith Exp $
+ * $Id: ppb_base.c,v 1.5 1998/09/13 18:26:26 nsouch Exp $
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <machine/clock.h>
#include <dev/ppbus/ppbconf.h>
@@ -70,28 +71,39 @@ int
ppb_poll_device(struct ppb_device *dev, int max,
char mask, char status, int how)
{
- int i, error;
+ int i, j, error;
+ char r;
+
+ /* try at least up to 10ms */
+ for (j = 0; j < ((how & PPB_POLL) ? max : 1); j++) {
+ for (i = 0; i < 10000; i++) {
+ r = ppb_rstr(dev);
+ DELAY(1);
+ if ((r & mask) == status)
+ return (0);
+ }
+ }
- for (i = 0; i < max; i++) {
+ if (!(how & PPB_POLL)) {
+ for (i = 0; max == PPB_FOREVER || i < max-1; i++) {
if ((ppb_rstr(dev) & mask) == status)
return (0);
switch (how) {
case PPB_NOINTR:
/* wait 10 ms */
- if ((error = tsleep((caddr_t)dev, PPBPRI,
- "ppbpoll", hz/100)))
- return (error);
+ tsleep((caddr_t)dev, PPBPRI, "ppbpoll", hz/100);
break;
case PPB_INTR:
default:
/* wait 10 ms */
if ((error = tsleep((caddr_t)dev, PPBPRI | PCATCH,
- "ppbpoll", hz/100)))
+ "ppbpoll", hz/100)) != EWOULDBLOCK)
return (error);
break;
}
+ }
}
return (EWOULDBLOCK);
@@ -108,16 +120,31 @@ 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))
+ if ((*ppb->ppb_link->adapter->setmode)(
+ ppb->ppb_link->adapter_unit, mode))
return (-1);
/* XXX yet device mode = ppbus mode = chipset mode */
- dev->mode = ppb->mode = mode;
+ dev->mode = ppb->mode = (mode & PPB_MASK);
return (old_mode);
}
/*
+ * ppb_write()
+ *
+ * Write charaters to the port
+ */
+int
+ppb_write(struct ppb_device *dev, char *buf, int len, int how)
+{
+ struct ppb_data *ppb = dev->ppb;
+
+ return (ppb->ppb_link->adapter->write(ppb->ppb_link->adapter_unit,
+ buf, len, how));
+}
+
+/*
* ppb_reset_epp_timeout()
*
* Reset the EPP timeout bit in the status register
@@ -130,7 +157,7 @@ ppb_reset_epp_timeout(struct ppb_device *dev)
if (ppb->ppb_owner != dev)
return (EACCES);
- (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit);
+ (*ppb->ppb_link->adapter->reset_epp_timeout)(ppb->ppb_link->adapter_unit);
return (0);
}
@@ -148,7 +175,7 @@ ppb_ecp_sync(struct ppb_device *dev)
if (ppb->ppb_owner != dev)
return (EACCES);
- (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit);
+ (*ppb->ppb_link->adapter->ecp_sync)(ppb->ppb_link->adapter_unit);
return (0);
}
diff --git a/sys/dev/ppbus/ppb_msq.c b/sys/dev/ppbus/ppb_msq.c
index 7e631e4..addcf04 100644
--- a/sys/dev/ppbus/ppb_msq.c
+++ b/sys/dev/ppbus/ppb_msq.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_msq.c,v 1.2 1998/09/13 18:26:26 nsouch Exp $
+ * $Id: ppb_msq.c,v 1.3 1998/09/20 14:41:54 nsouch Exp $
*
*/
#include <machine/stdarg.h>
@@ -272,9 +272,19 @@ ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret)
xfer = mode2xfer(dev, mi->opcode);
/* figure out if we should use ieee1284 code */
- if (!xfer->loop)
- panic("%s: IEEE1284 code not supported",
- __FUNCTION__);
+ if (!xfer->loop) {
+ if (mi->opcode == MS_OP_PUT) {
+ if ((error = ppb->ppb_link->adapter->write(
+ ppb->ppb_link->adapter_unit,
+ (char *)mi->arg[0].p,
+ mi->arg[1].i, 0)))
+ goto error;
+
+ INCR_PC;
+ goto next;
+ } else
+ panic("%s: IEEE1284 read not supported", __FUNCTION__);
+ }
/* XXX should use ppb_MS_init_msq() */
initxfer[0].arg[0].p = mi->arg[0].p;
@@ -309,10 +319,12 @@ ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret)
* is unknown here
*/
if ((error = ppb->ppb_link->adapter->exec_microseq(
- dev->id_unit, &mi)))
+ ppb->ppb_link->adapter_unit,
+ &mi)))
goto error;
break;
}
+ next:
}
error:
return (error);
diff --git a/sys/dev/ppbus/ppb_msq.h b/sys/dev/ppbus/ppb_msq.h
index cdc8154..4bba299 100644
--- a/sys/dev/ppbus/ppb_msq.h
+++ b/sys/dev/ppbus/ppb_msq.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_msq.h,v 1.1.2.7 1998/06/20 19:03:47 son Exp $
+ * $Id: ppb_msq.h,v 1.2 1998/09/13 18:26:26 nsouch Exp $
*
*/
#ifndef __PPB_MSQ_H
@@ -34,7 +34,7 @@
*/
/* microsequence parameter descriptor */
-#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position */
+#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position < 256 XXX */
#define MS_ARG_MASK 0x0f00 /* mask to retrieve the argument number */
#define MS_TYP_MASK 0xf000 /* mask to retrieve the type of the param */
diff --git a/sys/dev/ppbus/ppbconf.c b/sys/dev/ppbus/ppbconf.c
index 8f657cd..779db99 100644
--- a/sys/dev/ppbus/ppbconf.c
+++ b/sys/dev/ppbus/ppbconf.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997, 1998 Nicolas Souchu
+ * Copyright (c) 1997, 1998, 1999 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.c,v 1.8 1998/09/20 14:41:54 nsouch Exp $
+ * $Id: ppbconf.c,v 1.9 1998/12/07 21:58:16 archie Exp $
*
*/
#include <sys/param.h>
@@ -37,6 +37,8 @@
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/ppb_1284.h>
+#include "opt_ppb_1284.h"
+
LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */
/*
@@ -80,15 +82,30 @@ ppb_alloc_bus(void)
return(ppb);
}
+#define PPB_PNP_PRINTER 0
+#define PPB_PNP_MODEM 1
+#define PPB_PNP_NET 2
+#define PPB_PNP_HDC 3
+#define PPB_PNP_PCMCIA 4
+#define PPB_PNP_MEDIA 5
+#define PPB_PNP_FDC 6
+#define PPB_PNP_PORTS 7
+#define PPB_PNP_SCANNER 8
+#define PPB_PNP_DIGICAM 9
+
+#ifndef DONTPROBE_1284
+
static char *pnp_tokens[] = {
"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
+#if 0
static char *pnp_classes[] = {
"printer", "modem", "network device",
"hard disk", "PCMCIA", "multimedia device",
"floppy disk", "ports", "scanner",
"digital camera", "unknown device", NULL };
+#endif
/*
* search_token()
@@ -134,75 +151,40 @@ search_token(char *str, int slen, char *token)
* Returns the class id. of the peripherial, -1 otherwise
*/
static int
-ppb_pnp_detect(struct ppb_data *ppb)
+ppb_pnp_detect(struct ppb_data *ppb, struct ppb_device *pnpdev)
{
- char *token, *q, *class = 0;
+ char *token, *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 */
- bzero(&pnpdev, sizeof(pnpdev));
-
- pnpdev.ppb = ppb;
- if (bootverbose)
- printf("ppb: <PnP> probing devices on ppbus %d...\n",
+ printf("Probing for PnP 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);
- }
-
- if ((error = ppb_1284_negociate(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
- if (bootverbose)
- printf("ppb: <PnP> ppb_1284_negociate()=%d\n", error);
-
- goto end_detect;
- }
- len = 0;
- for (q=str; !(ppb_rstr(&pnpdev) & PERROR); q++) {
- if ((error = nibble_1284_inbyte(&pnpdev, q))) {
- if (bootverbose) {
- *q = '\0';
- printf("ppb: <PnP> len=%d, %s\n", len, str);
- 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");
- goto end_detect;
- }
- }
- *q = '\0';
-
- nibble_1284_sync(&pnpdev);
+ if ((error = ppb_1284_read_id(pnpdev, PPB_NIBBLE, str,
+ PPB_PnP_STRING_SIZE, &len)))
+ goto end_detect;
- if (bootverbose) {
- printf("ppb: <PnP> %d characters: ", len);
- for (i = 0; i < len; i++)
- printf("0x%x ", str[i]);
- printf("\n");
- }
+#ifdef DEBUG_1284
+ printf("ppb: <PnP> %d characters: ", len);
+ for (i = 0; i < len; i++)
+ printf("%c(0x%x) ", str[i], str[i]);
+ printf("\n");
+#endif
/* replace ';' characters by '\0' */
for (i = 0; i < len; i++)
str[i] = (str[i] == ';') ? '\0' : str[i];
- if ((token = search_token(str, len, "MFG")) != NULL)
+ if ((token = search_token(str, len, "MFG")) != NULL ||
+ (token = search_token(str, len, "MANUFACTURER")) != NULL)
printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit,
search_token(token, UNKNOWN_LENGTH, ":") + 1);
else
printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit);
- if ((token = search_token(str, len, "MDL")) != NULL)
+ if ((token = search_token(str, len, "MDL")) != NULL ||
+ (token = search_token(str, len, "MODEL")) != NULL)
printf(" %s",
search_token(token, UNKNOWN_LENGTH, ":") + 1);
else
@@ -223,7 +205,8 @@ ppb_pnp_detect(struct ppb_data *ppb)
printf(" %s", class);
}
- if ((token = search_token(str, len, "CMD")) != NULL)
+ if ((token = search_token(str, len, "CMD")) != NULL ||
+ (token = search_token(str, len, "COMMAND")) != NULL)
printf(" %s",
search_token(token, UNKNOWN_LENGTH, ":") + 1);
@@ -241,13 +224,116 @@ ppb_pnp_detect(struct ppb_data *ppb)
class_id = PPB_PnP_UNKNOWN;
end_detect:
- if ((error = ppb_1284_terminate(&pnpdev, VALID_STATE)) && bootverbose)
- printf("ppb: ppb_1284_terminate()=%d\n", error);
+ return (class_id);
+}
+
+/*
+ * ppb_scan_bus()
+ *
+ * Scan the ppbus for IEEE1284 compliant devices
+ */
+static int
+ppb_scan_bus(struct ppb_data *ppb)
+{
+ struct ppb_device pnpdev; /* temporary device to perform I/O */
+ int error = 0;
+
+ /* initialize the pnpdev structure for future use */
+ bzero(&pnpdev, sizeof(pnpdev));
+ pnpdev.ppb = ppb;
+
+ if ((error = ppb_request_bus(&pnpdev, PPB_DONTWAIT))) {
+ if (bootverbose)
+ printf("ppb: cannot allocate ppbus!\n");
+
+ return (error);
+ }
+
+ /* try all IEEE1284 modes, for one device only
+ *
+ * XXX We should implement the IEEE1284.3 standard to detect
+ * daisy chained devices
+ */
+
+ error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, PPB_REQUEST_ID);
+
+ if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284))
+ goto end_scan;
+
+ ppb_1284_terminate(&pnpdev);
+
+ printf("ppc%d: IEEE1284 device found ", ppb->ppb_link->adapter_unit);
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, 0))) {
+ printf("/NIBBLE");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_PS2, 0))) {
+ printf("/PS2");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, 0))) {
+ printf("/ECP");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, PPB_USE_RLE))) {
+ printf("/ECP_RLE");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_EPP, 0))) {
+ printf("/EPP");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+#if 0
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, PPB_REQUEST_ID))) {
+ printf("/NIBBLE_ID");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_PS2, PPB_REQUEST_ID))) {
+ printf("/PS2_ID");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, PPB_REQUEST_ID))) {
+ printf("/ECP_ID");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP,
+ PPB_REQUEST_ID | PPB_USE_RLE))) {
+ printf("/ECP_RLE_ID");
+ ppb_1284_terminate(&pnpdev);
+ }
+#endif
+
+ if (!(error = ppb_1284_negociate(&pnpdev, PPB_COMPATIBLE,
+ PPB_EXTENSIBILITY_LINK))) {
+ printf("/Extensibility Link");
+ ppb_1284_terminate(&pnpdev);
+ }
+
+ printf(" in FORWARD_IDLE state\n");
+
+ /* detect PnP devices */
+ ppb->class_id = ppb_pnp_detect(ppb, &pnpdev);
ppb_release_bus(&pnpdev);
- return (class_id);
+
+ return (0);
+
+end_scan:
+ ppb_release_bus(&pnpdev);
+ return (error);
}
+#endif /* !DONTPROBE_1284 */
+
/*
* ppb_attachdevs()
*
@@ -263,11 +349,10 @@ ppb_attachdevs(struct ppb_data *ppb)
LIST_INIT(&ppb->ppb_devs); /* initialise device/driver list */
p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
-/* XXX wait for ieee1284 good support */
-#if 0
- /* detect PnP devices */
- ppb->class_id = ppb_pnp_detect(ppb);
-#endif
+#ifndef DONTPROBE_1284
+ /* detect IEEE1284 compliant devices */
+ ppb_scan_bus(ppb);
+#endif /* !DONTPROBE_1284 */
/*
* Blindly try all probes here. Later we should look at
diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h
index 80efe28..80bfa7b 100644
--- a/sys/dev/ppbus/ppbconf.h
+++ b/sys/dev/ppbus/ppbconf.h
@@ -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.8 1998/09/13 18:26:26 nsouch Exp $
+ * $Id: ppbconf.h,v 1.9 1998/09/20 14:41:54 nsouch Exp $
*
*/
#ifndef __PPBCONF_H
@@ -47,7 +47,12 @@
#define PPB_EPP 0x4 /* EPP mode, 32 bit */
#define PPB_ECP 0x8 /* ECP mode */
-#define PPB_SPP PPB_NIBBLE|PPB_PS2
+/* mode aliases */
+#define PPB_SPP PPB_NIBBLE|PPB_PS2
+#define PPB_BYTE PPB_PS2
+
+#define PPB_MASK 0x0f
+#define PPB_OPTIONS_MASK 0xf0
#define PPB_IS_EPP(mode) (mode & PPB_EPP)
#define PPB_IN_EPP_MODE(dev) (PPB_IS_EPP (ppb_get_mode (dev)))
@@ -103,6 +108,8 @@ struct ppb_status {
#define PPB_NOINTR 0
#define PPB_WAIT 0x1
#define PPB_INTR 0x2
+#define PPB_POLL 0x4
+#define PPB_FOREVER -1
/*
* Microsequence stuff.
@@ -144,14 +151,14 @@ struct ppb_context {
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 */
+ ushort avm; /* available IEEE1284 modes of
+ * the device */
struct ppb_context ctx; /* context of the device */
@@ -184,6 +191,8 @@ struct ppb_adapter {
int (*exec_microseq)(int, struct ppb_microseq **);
int (*setmode)(int, int);
+ int (*read)(int, char *, int, int);
+ int (*write)(int, char *, int, int);
void (*outsb_epp)(int, char *, int);
void (*outsw_epp)(int, char *, int);
@@ -230,7 +239,7 @@ struct ppb_link {
/*
* Maximum size of the PnP info string
*/
-#define PPB_PnP_STRING_SIZE 128 /* XXX */
+#define PPB_PnP_STRING_SIZE 256 /* XXX */
/*
* Parallel Port Bus structure.
@@ -250,10 +259,11 @@ struct ppb_data {
#define PPB_PnP_UNKNOWN 10
int class_id; /* not a PnP device if class_id < 0 */
+ int state; /* current IEEE1284 state */
+ int error; /* last IEEE1284 error */
+
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 */
@@ -294,6 +304,7 @@ extern int ppb_ecp_sync(struct ppb_device *);
extern int ppb_get_status(struct ppb_device *, struct ppb_status *);
extern int ppb_set_mode(struct ppb_device *, int);
+extern int ppb_write(struct ppb_device *, char *, int, int);
/*
* These are defined as macros for speedup.
diff --git a/sys/dev/ppbus/ppi.c b/sys/dev/ppbus/ppi.c
index 3103ee3..e0072f3 100644
--- a/sys/dev/ppbus/ppi.c
+++ b/sys/dev/ppbus/ppi.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppi.c,v 1.7 1998/06/07 17:09:49 dfr Exp $
+ * $Id: ppi.c,v 1.8 1998/12/07 21:58:16 archie Exp $
*
*/
#include "ppi.h"
@@ -34,18 +34,35 @@
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
+#include <machine/clock.h>
+
#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+
+#include "opt_ppb_1284.h"
+
+#ifdef PERIPH_1284
+#include <dev/ppbus/ppb_1284.h>
+#endif
+
#include <dev/ppbus/ppi.h>
+#define BUFSIZE 512
struct ppi_data {
int ppi_unit;
int ppi_flags;
#define HAVE_PPBUS (1<<0)
+#define HAD_PPBUS (1<<1)
+
+ int ppi_count;
+ int ppi_mode; /* IEEE1284 mode */
+ char ppi_buffer[BUFSIZE];
struct ppb_device ppi_dev;
};
@@ -70,13 +87,41 @@ DATA_SET(ppbdriver_set, ppidriver);
static d_open_t ppiopen;
static d_close_t ppiclose;
static d_ioctl_t ppiioctl;
+static d_write_t ppiwrite;
+static d_read_t ppiread;
#define CDEV_MAJOR 82
static struct cdevsw ppi_cdevsw =
- { ppiopen, ppiclose, noread, nowrite, /* 82 */
+ { ppiopen, ppiclose, ppiread, ppiwrite, /* 82 */
ppiioctl, nullstop, nullreset, nodevtotty,
seltrue, nommap, nostrat, "ppi", NULL, -1 };
+#ifdef PERIPH_1284
+
+static void
+ppi_enable_intr(struct ppi_data *ppi)
+{
+ char r;
+
+ r = ppb_rctr(&ppi->ppi_dev);
+ ppb_wctr(&ppi->ppi_dev, r | IRQENABLE);
+
+ return;
+}
+
+static void
+ppi_disable_intr(struct ppi_data *ppi)
+{
+ char r;
+
+ r = ppb_rctr(&ppi->ppi_dev);
+ ppb_wctr(&ppi->ppi_dev, r & ~IRQENABLE);
+
+ return;
+}
+
+#endif /* PERIPH_1284 */
+
/*
* ppiprobe()
*/
@@ -125,9 +170,72 @@ ppiattach(struct ppb_device *dev)
return (1);
}
+/*
+ * Cable
+ * -----
+ *
+ * Use an IEEE1284 compliant (DB25/DB25) cable with the following tricks:
+ *
+ * nStrobe <-> nAck 1 <-> 10
+ * nAutofd <-> Busy 11 <-> 14
+ * nSelectin <-> Select 17 <-> 13
+ * nInit <-> nFault 15 <-> 16
+ *
+ */
static void
ppiintr(int unit)
{
+#ifdef PERIPH_1284
+ struct ppi_data *ppi = ppidata[unit];
+
+ ppi_disable_intr(ppi);
+
+ switch (ppi->ppi_dev.ppb->state) {
+
+ /* accept IEEE1284 negociation then wakeup an waiting process to
+ * continue negociation at process level */
+ case PPB_FORWARD_IDLE:
+ /* Event 1 */
+ if ((ppb_rstr(&ppi->ppi_dev) & (SELECT | nBUSY)) ==
+ (SELECT | nBUSY)) {
+ /* IEEE1284 negociation */
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+
+ /* Event 2 - prepare for reading the ext. value */
+ ppb_wctr(&ppi->ppi_dev, (PCD | STROBE | nINIT) & ~SELECTIN);
+
+ ppi->ppi_dev.ppb->state = PPB_NEGOCIATION;
+
+ } else {
+#ifdef DEBUG_1284
+ printf("0x%x", ppb_rstr(&ppi->ppi_dev));
+#endif
+ ppb_peripheral_terminate(&ppi->ppi_dev, PPB_DONTWAIT);
+ break;
+ }
+
+ /* wake up any process waiting for negociation from
+ * remote master host */
+
+ /* XXX should set a variable to warn the process about
+ * the interrupt */
+
+ wakeup(ppi);
+ break;
+ default:
+#ifdef DEBUG_1284
+ printf("?%d", ppi->ppi_dev.ppb->state);
+#endif
+ ppi->ppi_dev.ppb->state = PPB_FORWARD_IDLE;
+ ppb_set_mode(&ppi->ppi_dev, PPB_COMPATIBLE);
+ break;
+ }
+
+ ppi_enable_intr(ppi);
+#endif /* PERIPH_1284 */
+
return;
}
@@ -141,11 +249,16 @@ ppiopen(dev_t dev, int flags, int fmt, struct proc *p)
if (unit >= nppi)
return (ENXIO);
- if (!(ppi->ppi_flags & HAVE_PPBUS))
- if ((res = ppb_request_bus(&ppi->ppi_dev, (flags & O_NONBLOCK) ? PPB_DONTWAIT : (PPB_WAIT | PPB_INTR))))
+ if (!(ppi->ppi_flags & HAVE_PPBUS)) {
+ if ((res = ppb_request_bus(&ppi->ppi_dev,
+ (flags & O_NONBLOCK) ? PPB_DONTWAIT :
+ (PPB_WAIT | PPB_INTR))))
return (res);
- ppi->ppi_flags |= HAVE_PPBUS;
+ ppi->ppi_flags |= HAVE_PPBUS;
+ }
+ ppi->ppi_count += 1;
+
return (0);
}
@@ -155,12 +268,206 @@ ppiclose(dev_t dev, int flags, int fmt, struct proc *p)
u_int unit = minor(dev);
struct ppi_data *ppi = ppidata[unit];
- if (ppi->ppi_flags & HAVE_PPBUS)
+ ppi->ppi_count --;
+ if (!ppi->ppi_count) {
+
+#ifdef PERIPH_1284
+ switch (ppi->ppi_dev.ppb->state) {
+ case PPB_PERIPHERAL_IDLE:
+ ppb_peripheral_terminate(&ppi->ppi_dev, 0);
+ break;
+ case PPB_REVERSE_IDLE:
+ case PPB_EPP_IDLE:
+ case PPB_ECP_FORWARD_IDLE:
+ default:
+ ppb_1284_terminate(&ppi->ppi_dev);
+ break;
+ }
+#endif /* PERIPH_1284 */
+
ppb_release_bus(&ppi->ppi_dev);
- ppi->ppi_flags &= ~HAVE_PPBUS;
+ ppi->ppi_flags &= ~HAVE_PPBUS;
+ }
+
return (0);
}
+/*
+ * ppiread()
+ *
+ * IEEE1284 compliant read.
+ *
+ * First, try negociation to BYTE then NIBBLE mode
+ * If no data is available, wait for it otherwise transfer as much as possible
+ */
+static int
+ppiread(dev_t dev, struct uio *uio, int ioflag)
+{
+#ifdef PERIPH_1284
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = ppidata[unit];
+ int len, error = 0;
+
+ switch (ppi->ppi_dev.ppb->state) {
+ case PPB_PERIPHERAL_IDLE:
+ ppb_peripheral_terminate(&ppi->ppi_dev, 0);
+ /* fall throught */
+
+ case PPB_FORWARD_IDLE:
+ /* if can't negociate NIBBLE mode then try BYTE mode,
+ * the peripheral may be a computer
+ */
+ if ((ppb_1284_negociate(&ppi->ppi_dev,
+ ppi->ppi_mode = PPB_NIBBLE, 0))) {
+
+ /* XXX Wait 2 seconds to let the remote host some
+ * time to terminate its interrupt
+ */
+ tsleep(ppi, PPBPRI, "ppiread", 2*hz);
+
+ if ((error = ppb_1284_negociate(&ppi->ppi_dev,
+ ppi->ppi_mode = PPB_BYTE, 0)))
+ return (error);
+ }
+ break;
+
+ case PPB_REVERSE_IDLE:
+ case PPB_EPP_IDLE:
+ case PPB_ECP_FORWARD_IDLE:
+ default:
+ break;
+ }
+
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+ /* read data */
+ len = 0;
+ while (uio->uio_resid) {
+ if ((error = ppb_1284_read(&ppi->ppi_dev, ppi->ppi_mode,
+ ppi->ppi_buffer, min(BUFSIZE, uio->uio_resid),
+ &len))) {
+ goto error;
+ }
+
+ if (!len)
+ goto error; /* no more data */
+
+#ifdef DEBUG_1284
+ printf("d");
+#endif
+ if ((error = uiomove(ppi->ppi_buffer, len, uio)))
+ goto error;
+ }
+
+error:
+
+#else /* PERIPH_1284 */
+ int error = ENODEV;
+#endif
+
+ return (error);
+}
+
+/*
+ * ppiwrite()
+ *
+ * IEEE1284 compliant write
+ *
+ * Actually, this is the peripheral side of a remote IEEE1284 read
+ *
+ * The first part of the negociation (IEEE1284 device detection) is
+ * done at interrupt level, then the remaining is done by the writing
+ * process
+ *
+ * Once negociation done, transfer data
+ */
+static int
+ppiwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+#ifdef PERIPH_1284
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = ppidata[unit];
+ struct ppb_data *ppb = ppi->ppi_dev.ppb;
+ int len, error = 0, sent;
+
+#if 0
+ int ret;
+
+ #define ADDRESS MS_PARAM(0, 0, MS_TYP_PTR)
+ #define LENGTH MS_PARAM(0, 1, MS_TYP_INT)
+
+ struct ppb_microseq msq[] = {
+ { MS_OP_PUT, { MS_UNKNOWN, MS_UNKNOWN, MS_UNKNOWN } },
+ MS_RET(0)
+ };
+
+ /* negociate ECP mode */
+ if (ppb_1284_negociate(&ppi->ppi_dev, PPB_ECP, 0)) {
+ printf("ppiwrite: ECP negociation failed\n");
+ }
+
+ while (!error && (len = min(uio->uio_resid, BUFSIZE))) {
+ uiomove(ppi->ppi_buffer, len, uio);
+
+ ppb_MS_init_msq(msq, 2, ADDRESS, ppi->ppi_buffer, LENGTH, len);
+
+ error = ppb_MS_microseq(&ppi->ppi_dev, msq, &ret);
+ }
+#endif
+
+ /* we have to be peripheral to be able to send data, so
+ * wait for the appropriate state
+ */
+ if (ppb->state < PPB_PERIPHERAL_NEGOCIATION)
+ ppb_1284_terminate(&ppi->ppi_dev);
+
+ while (ppb->state != PPB_PERIPHERAL_IDLE) {
+ /* XXX should check a variable before sleeping */
+#ifdef DEBUG_1284
+ printf("s");
+#endif
+
+ ppi_enable_intr(ppi);
+
+ /* sleep until IEEE1284 negociation starts */
+ error = tsleep(ppi, PCATCH | PPBPRI, "ppiwrite", 0);
+
+ switch (error) {
+ case 0:
+ /* negociate peripheral side with BYTE mode */
+ ppb_peripheral_negociate(&ppi->ppi_dev, PPB_BYTE, 0);
+ break;
+ case EWOULDBLOCK:
+ break;
+ default:
+ goto error;
+ }
+ }
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+
+ /* negociation done, write bytes to master host */
+ while (len = min(uio->uio_resid, BUFSIZE)) {
+ uiomove(ppi->ppi_buffer, len, uio);
+ if ((error = byte_peripheral_write(&ppi->ppi_dev,
+ ppi->ppi_buffer, len, &sent)))
+ goto error;
+#ifdef DEBUG_1284
+ printf("d");
+#endif
+ }
+
+error:
+
+#else /* PERIPH_1284 */
+ int error = ENODEV;
+#endif
+
+ return (error);
+}
+
static int
ppiioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
@@ -208,7 +515,6 @@ ppiioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
case PPISFIFO: /* write FIFO */
ppb_wfifo(&ppi->ppi_dev, *val);
break;
-
default:
error = ENOTTY;
break;
diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c
index 82e9dab..17b67c5 100644
--- a/sys/dev/ppbus/vpo.c
+++ b/sys/dev/ppbus/vpo.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: vpo.c,v 1.9 1998/12/07 21:58:16 archie Exp $
+ * $Id: vpo.c,v 1.10 1999/01/09 18:05:46 nsouch Exp $
*
*/
@@ -51,6 +51,8 @@
#include <sys/kernel.h>
#endif /*KERNEL */
+#include "opt_vpo.h"
+
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/vpoio.h>
diff --git a/sys/dev/ppbus/vpoio.c b/sys/dev/ppbus/vpoio.c
index e42eca7..54791ee 100644
--- a/sys/dev/ppbus/vpoio.c
+++ b/sys/dev/ppbus/vpoio.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: vpoio.c,v 1.3 1998/09/20 14:41:54 nsouch Exp $
+ * $Id: vpoio.c,v 1.4 1998/12/07 21:58:16 archie Exp $
*
*/
@@ -41,6 +41,8 @@
#include <sys/kernel.h>
#endif /*KERNEL */
+#include "opt_vpo.h"
+
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/ppb_msq.h>
#include <dev/ppbus/vpoio.h>
@@ -284,8 +286,13 @@ vpoio_connect(struct vpoio_data *vpo, int how)
int error;
int ret;
- if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
+ if ((error = ppb_request_bus(&vpo->vpo_dev, how))) {
+
+#ifdef VP0_DEBUG
+ printf("%s: can't request bus!\n", __FUNCTION__);
+#endif
return error;
+ }
if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
@@ -437,8 +444,8 @@ vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
ppb_wctr(&vpo->vpo_dev,
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
}
- /* ppb_ecp_sync(&vpo->vpo_dev); */
#endif
+ ppb_ecp_sync(&vpo->vpo_dev);
return (error);
}
@@ -476,8 +483,8 @@ vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
ppb_wctr(&vpo->vpo_dev, PCD |
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
}
- /* ppb_ecp_sync(&vpo->vpo_dev); */
#endif
+ ppb_ecp_sync(&vpo->vpo_dev);
return (error);
}
@@ -701,6 +708,10 @@ 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)) {
+
+#ifdef VP0_DEBUG
+ printf("%s: not in disk mode!\n", __FUNCTION__);
+#endif
/* release ppbus */
vpoio_disconnect(vpo);
return (1);
diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c
index e351688..d2bc7a1 100644
--- a/sys/dev/ppc/ppc.c
+++ b/sys/dev/ppc/ppc.c
@@ -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.12 1998/12/07 21:58:22 archie Exp $
+ * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $
*
*/
#include "ppc.h"
@@ -49,6 +49,11 @@
#include <i386/isa/ppcreg.h>
+#include "opt_ppc.h"
+
+#define LOG_PPC(function, ppc, string) \
+ if (bootverbose) printf("%s: %s\n", function, string)
+
static int ppcprobe(struct isa_device *);
static int ppcattach(struct isa_device *);
@@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **);
static int ppc_generic_setmode(int, int);
static int ppc_smclike_setmode(int, int);
+static int ppc_read(int, char *, int, int);
+static int ppc_write(int, char *, int, int);
+
static struct ppb_adapter ppc_smclike_adapter = {
0, /* no intr handler, filled by chipset dependent code */
@@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = {
ppc_exec_microseq,
- ppc_smclike_setmode,
+ ppc_smclike_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = {
ppc_exec_microseq,
- ppc_generic_setmode,
+ ppc_generic_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) {
struct ppc_data *ppc = ppcdata[unit];
int i, r;
+ if (!(ppc->ppc_avm & PPB_ECP))
+ return;
+
r = r_ecr(ppc);
- if ((r & 0xe0) != 0x80)
+ if ((r & 0xe0) != PPC_ECR_EPP)
return;
for (i = 0; i < 100; i++) {
@@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) {
return;
}
-static void
-ppcintr(int unit)
+/*
+ * ppc_detect_fifo()
+ *
+ * Detect parallel port FIFO
+ */
+static int
+ppc_detect_fifo(struct ppc_data *ppc)
{
- /* call directly upper code */
- ppb_intr(&ppcdata[unit]->ppc_link);
+ char ecr_sav;
+ char ctr_sav, ctr, cc;
+ short i;
+
+ /* save registers */
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
- return;
+ /* enter ECP configuration mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xf4);
+
+ /* read PWord size - transfers in FIFO mode must be PWord aligned */
+ ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK);
+
+ /* XXX 16 and 32 bits implementations not supported */
+ if (ppc->ppc_pword != PPC_PWORD_8) {
+ LOG_PPC(__FUNCTION__, ppc, "PWord not supported");
+ goto error;
+ }
+
+ w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */
+ ctr = r_ctr(ppc);
+ w_ctr(ppc, ctr | PCD); /* set direction to 1 */
+
+ /* enter ECP test mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xd4);
+
+ /* flush the FIFO */
+ for (i=0; i<1024; i++) {
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ break;
+ cc = r_fifo(ppc);
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO");
+ goto error;
+ }
+
+ /* enable interrupts, no DMA */
+ w_ecr(ppc, 0xd0);
+
+ /* determine readIntrThreshold
+ * fill the FIFO until serviceIntr is set
+ */
+ for (i=0; i<1024; i++) {
+ w_fifo(ppc, (char)i);
+ if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) {
+ /* readThreshold reached */
+ ppc->ppc_rthr = i+1;
+ }
+ if (r_ecr(ppc) & PPC_FIFO_FULL) {
+ ppc->ppc_fifo = i+1;
+ break;
+ }
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO");
+ goto error;
+ }
+
+ w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */
+ w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */
+ w_ecr(ppc, 0xd0); /* enable interrupts */
+
+ /* determine writeIntrThreshold
+ * empty the FIFO until serviceIntr is set
+ */
+ for (i=ppc->ppc_fifo; i>0; i--) {
+ if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) {
+ LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO");
+ goto error;
+ }
+ if (r_ecr(ppc) & PPC_SERVICE_INTR) {
+ /* writeIntrThreshold reached */
+ ppc->ppc_wthr = ppc->ppc_fifo - i+1;
+ }
+ /* if FIFO empty before the last byte, error */
+ if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO");
+ goto error;
+ }
+ }
+
+ /* FIFO must be empty after the last byte */
+ if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO");
+ goto error;
+ }
+
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (0);
+
+error:
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (EINVAL);
}
static int
@@ -654,6 +767,13 @@ config:
ppc->ppc_avm = chipset_mode;
}
+ /* set FIFO threshold to 16 */
+ if (ppc->ppc_avm & PPB_ECP) {
+ /* select CRA */
+ outb(csr, 0xa);
+ outb(cio, 16);
+ }
+
end_detect:
if (bootverbose)
@@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
if (!chipset_mode) {
/* first, check for ECP */
- w_ecr(ppc, 0x20);
- if ((r_ecr(ppc) & 0xe0) == 0x20) {
+ w_ecr(ppc, PPC_ECR_PS2);
+ if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
if (bootverbose)
printf(" ECP SPP");
/* search for SMC style ECP+EPP mode */
- w_ecr(ppc, 0x80);
+ w_ecr(ppc, PPC_ECR_EPP);
}
/* try to reset EPP timeout bit */
@@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
}
} else {
/* restore to standard mode */
- w_ecr(ppc, 0x0);
+ w_ecr(ppc, PPC_ECR_STD);
}
/* XXX try to detect NIBBLE and PS2 modes */
@@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) {
}
}
+ /* configure/detect ECP FIFO */
+ if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80))
+ ppc_detect_fifo(ppc);
+
return (0);
}
@@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq)
/* unreached */
}
+static void
+ppcintr(int unit)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ctr, ecr;
+
+ ctr = r_ctr(ppc);
+ ecr = r_ecr(ppc);
+
+#ifdef PPC_DEBUG
+ printf("!");
+#endif
+
+ /* don't use ecp mode with IRQENABLE set */
+ if (ctr & IRQENABLE) {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+
+ if (ctr & nFAULT) {
+ if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) {
+
+ w_ecr(ppc, ecr | PPC_nFAULT_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT;
+ } else {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+ }
+
+ if (ppc->ppc_irqstat & PPC_IRQ_DMA) {
+ /* disable interrupts (should be done by hardware though) */
+ w_ecr(ppc, ecr | PPC_SERVICE_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_DMA;
+ ecr = r_ecr(ppc);
+
+ /* check if DMA completed */
+ if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) {
+#ifdef PPC_DEBUG
+ printf("a");
+#endif
+ /* stop DMA */
+ w_ecr(ppc, ecr & ~PPC_ENABLE_DMA);
+ ecr = r_ecr(ppc);
+
+ if (ppc->ppc_dmastat == PPC_DMA_STARTED) {
+#ifdef PPC_DEBUG
+ printf("d");
+#endif
+ isa_dmadone(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+
+ ppc->ppc_dmastat = PPC_DMA_COMPLETE;
+
+ /* wakeup the waiting process */
+ wakeup((caddr_t)ppc);
+ }
+ }
+ } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) {
+
+ /* classic interrupt I/O */
+ ppc->ppc_irqstat &= ~PPC_IRQ_FIFO;
+
+ }
+
+ return;
+}
+
+static int
+ppc_read(int unit, char *buf, int len, int mode)
+{
+ return (EINVAL);
+}
+
+/*
+ * Call this function if you want to send data in any advanced mode
+ * of your parallel port: FIFO, DMA
+ *
+ * If what you want is not possible (no ECP, no DMA...),
+ * EINVAL is returned
+ */
+static int
+ppc_write(int unit, char *buf, int len, int how)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ecr, ecr_sav, ctr, ctr_sav;
+ int s, error = 0;
+ int spin;
+
+#ifdef PPC_DEBUG
+ printf("w");
+#endif
+
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
+
+ /*
+ * Send buffer with DMA, FIFO and interrupts
+ */
+ if (ppc->ppc_avm & PPB_ECP) {
+
+ if (ppc->ppc_dmachan >= 0) {
+
+ /* byte mode, no intr, no DMA, dir=0, flush fifo
+ */
+ ecr = PPC_ECR_STD | PPC_DISABLE_INTR;
+ w_ecr(ppc, ecr);
+
+ /* disable nAck interrupts */
+ ctr = r_ctr(ppc);
+ ctr &= ~IRQENABLE;
+ w_ctr(ppc, ctr);
+
+ ppc->ppc_dmaflags = 0;
+ ppc->ppc_dmaddr = (caddr_t)buf;
+ ppc->ppc_dmacnt = (u_int)len;
+
+ switch (ppc->ppc_mode) {
+ case PPB_COMPATIBLE:
+ /* compatible mode with FIFO, no intr, DMA, dir=0 */
+ ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ case PPB_ECP:
+ ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ default:
+ error = EINVAL;
+ goto error;
+ }
+
+ w_ecr(ppc, ecr);
+ ecr = r_ecr(ppc);
+
+ /* enter splhigh() not to be preempted
+ * by the dma interrupt, we may miss
+ * the wakeup otherwise
+ */
+ s = splhigh();
+
+ ppc->ppc_dmastat = PPC_DMA_INIT;
+
+ /* enable interrupts */
+ ecr &= ~PPC_SERVICE_INTR;
+ ppc->ppc_irqstat = PPC_IRQ_DMA;
+ w_ecr(ppc, ecr);
+
+ isa_dmastart(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+#ifdef PPC_DEBUG
+ printf("s%d", ppc->ppc_dmacnt);
+#endif
+ ppc->ppc_dmastat = PPC_DMA_STARTED;
+
+ /* Wait for the DMA completed interrupt. We hope we won't
+ * miss it, otherwise a signal will be necessary to unlock the
+ * process.
+ */
+ do {
+ /* release CPU */
+ error = tsleep((caddr_t)ppc,
+ PPBPRI | PCATCH, "ppcdma", 0);
+
+ } while (error == EWOULDBLOCK);
+
+ splx(s);
+
+ if (error) {
+#ifdef PPC_DEBUG
+ printf("i");
+#endif
+ /* stop DMA */
+ isa_dmadone(
+ ppc->ppc_dmaflags, ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt, ppc->ppc_dmachan);
+
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ goto error;
+ }
+
+ /* wait for an empty fifo */
+ while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+
+ for (spin=100; spin; spin--)
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ goto fifo_empty;
+#ifdef PPC_DEBUG
+ printf("Z");
+#endif
+ error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100);
+ if (error != EWOULDBLOCK) {
+#ifdef PPC_DEBUG
+ printf("I");
+#endif
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ error = EINTR;
+ goto error;
+ }
+ }
+
+fifo_empty:
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ } else
+ error = EINVAL; /* XXX we should FIFO and
+ * interrupts */
+ } else
+ error = EINVAL;
+
+error:
+
+ /* PDRQ must be kept unasserted until nPDACK is
+ * deasserted for a minimum of 350ns (SMC datasheet)
+ *
+ * Consequence may be a FIFO that never empty
+ */
+ DELAY(1);
+
+ w_ecr(ppc, ecr_sav);
+ w_ctr(ppc, ctr_sav);
+
+ return (error);
+}
+
/*
* Configure current operating mode
*/
@@ -1209,32 +1571,34 @@ 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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
- return (EOPNOTSUPP);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ return (EINVAL);
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
@@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode)
return (0);
}
+/*
+ * The ppc driver is free to choose options like FIFO or DMA
+ * if ECP mode is available.
+ *
+ * The 'RAW' option allows the upper drivers to force the ppc mode
+ * even with FIFO, DMA available.
+ */
int
ppc_smclike_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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP or EPP mode
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
/* select EPP mode */
- w_ecr(ppc, 0x80);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ ecr |= PPC_ECR_EPP;
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
-
return (0);
}
@@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp)
if((next_bios_ppc < BIOS_MAX_PPC) &&
(*(BIOS_PORTS+next_bios_ppc) != 0) ) {
dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++);
- printf("ppc: parallel port found at 0x%x\n",
- dvp->id_iobase);
+ if (bootverbose)
+ printf("ppc: parallel port found at 0x%x\n",
+ dvp->id_iobase);
} else
return (0);
}
@@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp)
if (!(dvp->id_flags & 0x20))
ppc->ppc_irq = ffs(dvp->id_irq) - 1;
+ ppc->ppc_dmachan = dvp->id_drq;
+
ppcdata[ppc->ppc_unit] = ppc;
nppc ++;
@@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp)
ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ?
ppc_epp_protocol[ppc->ppc_epp] : "");
+ if (ppc->ppc_fifo)
+ printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n",
+ ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr,
+ ppc->ppc_rthr);
+
isdp->id_ointr = ppcintr;
/*
@@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp)
ppc->ppc_link.ppbus = ppbus;
ppbus->ppb_link = &ppc->ppc_link;
+ if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) {
+
+ /* acquire the DMA channel forever */
+ isa_dma_acquire(ppc->ppc_dmachan);
+ isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */
+ }
+
/*
* Probe the ppbus and attach devices found.
*/
diff --git a/sys/dev/ppc/ppcreg.h b/sys/dev/ppc/ppcreg.h
index af8c9db..829bfd1 100644
--- a/sys/dev/ppc/ppcreg.h
+++ b/sys/dev/ppc/ppcreg.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $
+ * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $
*
*/
#ifndef __PPCREG_H
@@ -55,6 +55,34 @@ struct ppc_data {
int ppc_mode; /* chipset current mode */
int ppc_avm; /* chipset available modes */
+#define PPC_IRQ_NONE 0x0
+#define PPC_IRQ_nACK 0x1
+#define PPC_IRQ_DMA 0x2
+#define PPC_IRQ_FIFO 0x4
+#define PPC_IRQ_nFAULT 0x8
+ int ppc_irqstat; /* remind irq settings */
+
+#define PPC_DMA_INIT 0x01
+#define PPC_DMA_STARTED 0x02
+#define PPC_DMA_COMPLETE 0x03
+#define PPC_DMA_INTERRUPTED 0x04
+#define PPC_DMA_ERROR 0x05
+ int ppc_dmastat; /* dma state */
+ int ppc_dmachan; /* dma channel */
+ int ppc_dmaflags; /* dma transfer flags */
+ caddr_t ppc_dmaddr; /* buffer address */
+ u_int ppc_dmacnt; /* count of bytes sent with dma */
+
+#define PPC_PWORD_MASK 0x30
+#define PPC_PWORD_16 0x00
+#define PPC_PWORD_8 0x10
+#define PPC_PWORD_32 0x20
+ char ppc_pword; /* PWord size */
+ short ppc_fifo; /* FIFO threshold */
+
+ short ppc_wthr; /* writeIntrThresold */
+ short ppc_rthr; /* readIntrThresold */
+
#define ppc_base ppc_link.base
#define ppc_epp ppc_link.epp_protocol
#define ppc_irq ppc_link.id_irq
@@ -71,25 +99,44 @@ struct ppc_data {
* Parallel Port Chipset registers.
*/
#define PPC_SPP_DTR 0 /* SPP data register */
+#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */
#define PPC_SPP_STR 1 /* SPP status register */
#define PPC_SPP_CTR 2 /* SPP control register */
#define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */
-#define PPC_ECP_FIFO 0x400 /* ECP fifo register */
+#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */
+#define PPC_ECP_CNFGA 0x400 /* Configuration register A */
+#define PPC_ECP_CNFGB 0x401 /* Configuration register B */
#define PPC_ECP_ECR 0x402 /* ECP extended control register */
+#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */
+#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */
+#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */
+#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */
+#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */
+#define PPC_ECR_STD 0x0
+#define PPC_ECR_PS2 0x20
+#define PPC_ECR_FIFO 0x40
+#define PPC_ECR_ECP 0x60
+#define PPC_ECR_EPP 0x80
+
+#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR)
+#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR)
+
#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_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA))
+#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB))
#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 r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_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)
#define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte)
#define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte)
#define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte)
-#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte)
+#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte)
/*
* Register defines for the PC873xx parts
diff --git a/sys/i386/isa/ppc.c b/sys/i386/isa/ppc.c
index e351688..d2bc7a1 100644
--- a/sys/i386/isa/ppc.c
+++ b/sys/i386/isa/ppc.c
@@ -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.12 1998/12/07 21:58:22 archie Exp $
+ * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $
*
*/
#include "ppc.h"
@@ -49,6 +49,11 @@
#include <i386/isa/ppcreg.h>
+#include "opt_ppc.h"
+
+#define LOG_PPC(function, ppc, string) \
+ if (bootverbose) printf("%s: %s\n", function, string)
+
static int ppcprobe(struct isa_device *);
static int ppcattach(struct isa_device *);
@@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **);
static int ppc_generic_setmode(int, int);
static int ppc_smclike_setmode(int, int);
+static int ppc_read(int, char *, int, int);
+static int ppc_write(int, char *, int, int);
+
static struct ppb_adapter ppc_smclike_adapter = {
0, /* no intr handler, filled by chipset dependent code */
@@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = {
ppc_exec_microseq,
- ppc_smclike_setmode,
+ ppc_smclike_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = {
ppc_exec_microseq,
- ppc_generic_setmode,
+ ppc_generic_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) {
struct ppc_data *ppc = ppcdata[unit];
int i, r;
+ if (!(ppc->ppc_avm & PPB_ECP))
+ return;
+
r = r_ecr(ppc);
- if ((r & 0xe0) != 0x80)
+ if ((r & 0xe0) != PPC_ECR_EPP)
return;
for (i = 0; i < 100; i++) {
@@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) {
return;
}
-static void
-ppcintr(int unit)
+/*
+ * ppc_detect_fifo()
+ *
+ * Detect parallel port FIFO
+ */
+static int
+ppc_detect_fifo(struct ppc_data *ppc)
{
- /* call directly upper code */
- ppb_intr(&ppcdata[unit]->ppc_link);
+ char ecr_sav;
+ char ctr_sav, ctr, cc;
+ short i;
+
+ /* save registers */
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
- return;
+ /* enter ECP configuration mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xf4);
+
+ /* read PWord size - transfers in FIFO mode must be PWord aligned */
+ ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK);
+
+ /* XXX 16 and 32 bits implementations not supported */
+ if (ppc->ppc_pword != PPC_PWORD_8) {
+ LOG_PPC(__FUNCTION__, ppc, "PWord not supported");
+ goto error;
+ }
+
+ w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */
+ ctr = r_ctr(ppc);
+ w_ctr(ppc, ctr | PCD); /* set direction to 1 */
+
+ /* enter ECP test mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xd4);
+
+ /* flush the FIFO */
+ for (i=0; i<1024; i++) {
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ break;
+ cc = r_fifo(ppc);
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO");
+ goto error;
+ }
+
+ /* enable interrupts, no DMA */
+ w_ecr(ppc, 0xd0);
+
+ /* determine readIntrThreshold
+ * fill the FIFO until serviceIntr is set
+ */
+ for (i=0; i<1024; i++) {
+ w_fifo(ppc, (char)i);
+ if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) {
+ /* readThreshold reached */
+ ppc->ppc_rthr = i+1;
+ }
+ if (r_ecr(ppc) & PPC_FIFO_FULL) {
+ ppc->ppc_fifo = i+1;
+ break;
+ }
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO");
+ goto error;
+ }
+
+ w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */
+ w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */
+ w_ecr(ppc, 0xd0); /* enable interrupts */
+
+ /* determine writeIntrThreshold
+ * empty the FIFO until serviceIntr is set
+ */
+ for (i=ppc->ppc_fifo; i>0; i--) {
+ if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) {
+ LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO");
+ goto error;
+ }
+ if (r_ecr(ppc) & PPC_SERVICE_INTR) {
+ /* writeIntrThreshold reached */
+ ppc->ppc_wthr = ppc->ppc_fifo - i+1;
+ }
+ /* if FIFO empty before the last byte, error */
+ if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO");
+ goto error;
+ }
+ }
+
+ /* FIFO must be empty after the last byte */
+ if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO");
+ goto error;
+ }
+
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (0);
+
+error:
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (EINVAL);
}
static int
@@ -654,6 +767,13 @@ config:
ppc->ppc_avm = chipset_mode;
}
+ /* set FIFO threshold to 16 */
+ if (ppc->ppc_avm & PPB_ECP) {
+ /* select CRA */
+ outb(csr, 0xa);
+ outb(cio, 16);
+ }
+
end_detect:
if (bootverbose)
@@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
if (!chipset_mode) {
/* first, check for ECP */
- w_ecr(ppc, 0x20);
- if ((r_ecr(ppc) & 0xe0) == 0x20) {
+ w_ecr(ppc, PPC_ECR_PS2);
+ if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
if (bootverbose)
printf(" ECP SPP");
/* search for SMC style ECP+EPP mode */
- w_ecr(ppc, 0x80);
+ w_ecr(ppc, PPC_ECR_EPP);
}
/* try to reset EPP timeout bit */
@@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
}
} else {
/* restore to standard mode */
- w_ecr(ppc, 0x0);
+ w_ecr(ppc, PPC_ECR_STD);
}
/* XXX try to detect NIBBLE and PS2 modes */
@@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) {
}
}
+ /* configure/detect ECP FIFO */
+ if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80))
+ ppc_detect_fifo(ppc);
+
return (0);
}
@@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq)
/* unreached */
}
+static void
+ppcintr(int unit)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ctr, ecr;
+
+ ctr = r_ctr(ppc);
+ ecr = r_ecr(ppc);
+
+#ifdef PPC_DEBUG
+ printf("!");
+#endif
+
+ /* don't use ecp mode with IRQENABLE set */
+ if (ctr & IRQENABLE) {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+
+ if (ctr & nFAULT) {
+ if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) {
+
+ w_ecr(ppc, ecr | PPC_nFAULT_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT;
+ } else {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+ }
+
+ if (ppc->ppc_irqstat & PPC_IRQ_DMA) {
+ /* disable interrupts (should be done by hardware though) */
+ w_ecr(ppc, ecr | PPC_SERVICE_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_DMA;
+ ecr = r_ecr(ppc);
+
+ /* check if DMA completed */
+ if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) {
+#ifdef PPC_DEBUG
+ printf("a");
+#endif
+ /* stop DMA */
+ w_ecr(ppc, ecr & ~PPC_ENABLE_DMA);
+ ecr = r_ecr(ppc);
+
+ if (ppc->ppc_dmastat == PPC_DMA_STARTED) {
+#ifdef PPC_DEBUG
+ printf("d");
+#endif
+ isa_dmadone(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+
+ ppc->ppc_dmastat = PPC_DMA_COMPLETE;
+
+ /* wakeup the waiting process */
+ wakeup((caddr_t)ppc);
+ }
+ }
+ } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) {
+
+ /* classic interrupt I/O */
+ ppc->ppc_irqstat &= ~PPC_IRQ_FIFO;
+
+ }
+
+ return;
+}
+
+static int
+ppc_read(int unit, char *buf, int len, int mode)
+{
+ return (EINVAL);
+}
+
+/*
+ * Call this function if you want to send data in any advanced mode
+ * of your parallel port: FIFO, DMA
+ *
+ * If what you want is not possible (no ECP, no DMA...),
+ * EINVAL is returned
+ */
+static int
+ppc_write(int unit, char *buf, int len, int how)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ecr, ecr_sav, ctr, ctr_sav;
+ int s, error = 0;
+ int spin;
+
+#ifdef PPC_DEBUG
+ printf("w");
+#endif
+
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
+
+ /*
+ * Send buffer with DMA, FIFO and interrupts
+ */
+ if (ppc->ppc_avm & PPB_ECP) {
+
+ if (ppc->ppc_dmachan >= 0) {
+
+ /* byte mode, no intr, no DMA, dir=0, flush fifo
+ */
+ ecr = PPC_ECR_STD | PPC_DISABLE_INTR;
+ w_ecr(ppc, ecr);
+
+ /* disable nAck interrupts */
+ ctr = r_ctr(ppc);
+ ctr &= ~IRQENABLE;
+ w_ctr(ppc, ctr);
+
+ ppc->ppc_dmaflags = 0;
+ ppc->ppc_dmaddr = (caddr_t)buf;
+ ppc->ppc_dmacnt = (u_int)len;
+
+ switch (ppc->ppc_mode) {
+ case PPB_COMPATIBLE:
+ /* compatible mode with FIFO, no intr, DMA, dir=0 */
+ ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ case PPB_ECP:
+ ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ default:
+ error = EINVAL;
+ goto error;
+ }
+
+ w_ecr(ppc, ecr);
+ ecr = r_ecr(ppc);
+
+ /* enter splhigh() not to be preempted
+ * by the dma interrupt, we may miss
+ * the wakeup otherwise
+ */
+ s = splhigh();
+
+ ppc->ppc_dmastat = PPC_DMA_INIT;
+
+ /* enable interrupts */
+ ecr &= ~PPC_SERVICE_INTR;
+ ppc->ppc_irqstat = PPC_IRQ_DMA;
+ w_ecr(ppc, ecr);
+
+ isa_dmastart(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+#ifdef PPC_DEBUG
+ printf("s%d", ppc->ppc_dmacnt);
+#endif
+ ppc->ppc_dmastat = PPC_DMA_STARTED;
+
+ /* Wait for the DMA completed interrupt. We hope we won't
+ * miss it, otherwise a signal will be necessary to unlock the
+ * process.
+ */
+ do {
+ /* release CPU */
+ error = tsleep((caddr_t)ppc,
+ PPBPRI | PCATCH, "ppcdma", 0);
+
+ } while (error == EWOULDBLOCK);
+
+ splx(s);
+
+ if (error) {
+#ifdef PPC_DEBUG
+ printf("i");
+#endif
+ /* stop DMA */
+ isa_dmadone(
+ ppc->ppc_dmaflags, ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt, ppc->ppc_dmachan);
+
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ goto error;
+ }
+
+ /* wait for an empty fifo */
+ while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+
+ for (spin=100; spin; spin--)
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ goto fifo_empty;
+#ifdef PPC_DEBUG
+ printf("Z");
+#endif
+ error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100);
+ if (error != EWOULDBLOCK) {
+#ifdef PPC_DEBUG
+ printf("I");
+#endif
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ error = EINTR;
+ goto error;
+ }
+ }
+
+fifo_empty:
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ } else
+ error = EINVAL; /* XXX we should FIFO and
+ * interrupts */
+ } else
+ error = EINVAL;
+
+error:
+
+ /* PDRQ must be kept unasserted until nPDACK is
+ * deasserted for a minimum of 350ns (SMC datasheet)
+ *
+ * Consequence may be a FIFO that never empty
+ */
+ DELAY(1);
+
+ w_ecr(ppc, ecr_sav);
+ w_ctr(ppc, ctr_sav);
+
+ return (error);
+}
+
/*
* Configure current operating mode
*/
@@ -1209,32 +1571,34 @@ 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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
- return (EOPNOTSUPP);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ return (EINVAL);
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
@@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode)
return (0);
}
+/*
+ * The ppc driver is free to choose options like FIFO or DMA
+ * if ECP mode is available.
+ *
+ * The 'RAW' option allows the upper drivers to force the ppc mode
+ * even with FIFO, DMA available.
+ */
int
ppc_smclike_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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP or EPP mode
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
/* select EPP mode */
- w_ecr(ppc, 0x80);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ ecr |= PPC_ECR_EPP;
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
-
return (0);
}
@@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp)
if((next_bios_ppc < BIOS_MAX_PPC) &&
(*(BIOS_PORTS+next_bios_ppc) != 0) ) {
dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++);
- printf("ppc: parallel port found at 0x%x\n",
- dvp->id_iobase);
+ if (bootverbose)
+ printf("ppc: parallel port found at 0x%x\n",
+ dvp->id_iobase);
} else
return (0);
}
@@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp)
if (!(dvp->id_flags & 0x20))
ppc->ppc_irq = ffs(dvp->id_irq) - 1;
+ ppc->ppc_dmachan = dvp->id_drq;
+
ppcdata[ppc->ppc_unit] = ppc;
nppc ++;
@@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp)
ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ?
ppc_epp_protocol[ppc->ppc_epp] : "");
+ if (ppc->ppc_fifo)
+ printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n",
+ ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr,
+ ppc->ppc_rthr);
+
isdp->id_ointr = ppcintr;
/*
@@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp)
ppc->ppc_link.ppbus = ppbus;
ppbus->ppb_link = &ppc->ppc_link;
+ if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) {
+
+ /* acquire the DMA channel forever */
+ isa_dma_acquire(ppc->ppc_dmachan);
+ isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */
+ }
+
/*
* Probe the ppbus and attach devices found.
*/
diff --git a/sys/i386/isa/ppcreg.h b/sys/i386/isa/ppcreg.h
index af8c9db..829bfd1 100644
--- a/sys/i386/isa/ppcreg.h
+++ b/sys/i386/isa/ppcreg.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $
+ * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $
*
*/
#ifndef __PPCREG_H
@@ -55,6 +55,34 @@ struct ppc_data {
int ppc_mode; /* chipset current mode */
int ppc_avm; /* chipset available modes */
+#define PPC_IRQ_NONE 0x0
+#define PPC_IRQ_nACK 0x1
+#define PPC_IRQ_DMA 0x2
+#define PPC_IRQ_FIFO 0x4
+#define PPC_IRQ_nFAULT 0x8
+ int ppc_irqstat; /* remind irq settings */
+
+#define PPC_DMA_INIT 0x01
+#define PPC_DMA_STARTED 0x02
+#define PPC_DMA_COMPLETE 0x03
+#define PPC_DMA_INTERRUPTED 0x04
+#define PPC_DMA_ERROR 0x05
+ int ppc_dmastat; /* dma state */
+ int ppc_dmachan; /* dma channel */
+ int ppc_dmaflags; /* dma transfer flags */
+ caddr_t ppc_dmaddr; /* buffer address */
+ u_int ppc_dmacnt; /* count of bytes sent with dma */
+
+#define PPC_PWORD_MASK 0x30
+#define PPC_PWORD_16 0x00
+#define PPC_PWORD_8 0x10
+#define PPC_PWORD_32 0x20
+ char ppc_pword; /* PWord size */
+ short ppc_fifo; /* FIFO threshold */
+
+ short ppc_wthr; /* writeIntrThresold */
+ short ppc_rthr; /* readIntrThresold */
+
#define ppc_base ppc_link.base
#define ppc_epp ppc_link.epp_protocol
#define ppc_irq ppc_link.id_irq
@@ -71,25 +99,44 @@ struct ppc_data {
* Parallel Port Chipset registers.
*/
#define PPC_SPP_DTR 0 /* SPP data register */
+#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */
#define PPC_SPP_STR 1 /* SPP status register */
#define PPC_SPP_CTR 2 /* SPP control register */
#define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */
-#define PPC_ECP_FIFO 0x400 /* ECP fifo register */
+#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */
+#define PPC_ECP_CNFGA 0x400 /* Configuration register A */
+#define PPC_ECP_CNFGB 0x401 /* Configuration register B */
#define PPC_ECP_ECR 0x402 /* ECP extended control register */
+#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */
+#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */
+#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */
+#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */
+#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */
+#define PPC_ECR_STD 0x0
+#define PPC_ECR_PS2 0x20
+#define PPC_ECR_FIFO 0x40
+#define PPC_ECR_ECP 0x60
+#define PPC_ECR_EPP 0x80
+
+#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR)
+#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR)
+
#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_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA))
+#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB))
#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 r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_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)
#define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte)
#define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte)
#define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte)
-#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte)
+#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte)
/*
* Register defines for the PC873xx parts
diff --git a/sys/isa/ppc.c b/sys/isa/ppc.c
index e351688..d2bc7a1 100644
--- a/sys/isa/ppc.c
+++ b/sys/isa/ppc.c
@@ -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.12 1998/12/07 21:58:22 archie Exp $
+ * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $
*
*/
#include "ppc.h"
@@ -49,6 +49,11 @@
#include <i386/isa/ppcreg.h>
+#include "opt_ppc.h"
+
+#define LOG_PPC(function, ppc, string) \
+ if (bootverbose) printf("%s: %s\n", function, string)
+
static int ppcprobe(struct isa_device *);
static int ppcattach(struct isa_device *);
@@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **);
static int ppc_generic_setmode(int, int);
static int ppc_smclike_setmode(int, int);
+static int ppc_read(int, char *, int, int);
+static int ppc_write(int, char *, int, int);
+
static struct ppb_adapter ppc_smclike_adapter = {
0, /* no intr handler, filled by chipset dependent code */
@@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = {
ppc_exec_microseq,
- ppc_smclike_setmode,
+ ppc_smclike_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = {
ppc_exec_microseq,
- ppc_generic_setmode,
+ ppc_generic_setmode, ppc_read, ppc_write,
ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp,
ppc_insb_epp, ppc_insw_epp, ppc_insl_epp,
@@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) {
struct ppc_data *ppc = ppcdata[unit];
int i, r;
+ if (!(ppc->ppc_avm & PPB_ECP))
+ return;
+
r = r_ecr(ppc);
- if ((r & 0xe0) != 0x80)
+ if ((r & 0xe0) != PPC_ECR_EPP)
return;
for (i = 0; i < 100; i++) {
@@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) {
return;
}
-static void
-ppcintr(int unit)
+/*
+ * ppc_detect_fifo()
+ *
+ * Detect parallel port FIFO
+ */
+static int
+ppc_detect_fifo(struct ppc_data *ppc)
{
- /* call directly upper code */
- ppb_intr(&ppcdata[unit]->ppc_link);
+ char ecr_sav;
+ char ctr_sav, ctr, cc;
+ short i;
+
+ /* save registers */
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
- return;
+ /* enter ECP configuration mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xf4);
+
+ /* read PWord size - transfers in FIFO mode must be PWord aligned */
+ ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK);
+
+ /* XXX 16 and 32 bits implementations not supported */
+ if (ppc->ppc_pword != PPC_PWORD_8) {
+ LOG_PPC(__FUNCTION__, ppc, "PWord not supported");
+ goto error;
+ }
+
+ w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */
+ ctr = r_ctr(ppc);
+ w_ctr(ppc, ctr | PCD); /* set direction to 1 */
+
+ /* enter ECP test mode, no interrupt, no DMA */
+ w_ecr(ppc, 0xd4);
+
+ /* flush the FIFO */
+ for (i=0; i<1024; i++) {
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ break;
+ cc = r_fifo(ppc);
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO");
+ goto error;
+ }
+
+ /* enable interrupts, no DMA */
+ w_ecr(ppc, 0xd0);
+
+ /* determine readIntrThreshold
+ * fill the FIFO until serviceIntr is set
+ */
+ for (i=0; i<1024; i++) {
+ w_fifo(ppc, (char)i);
+ if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) {
+ /* readThreshold reached */
+ ppc->ppc_rthr = i+1;
+ }
+ if (r_ecr(ppc) & PPC_FIFO_FULL) {
+ ppc->ppc_fifo = i+1;
+ break;
+ }
+ }
+
+ if (i >= 1024) {
+ LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO");
+ goto error;
+ }
+
+ w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */
+ w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */
+ w_ecr(ppc, 0xd0); /* enable interrupts */
+
+ /* determine writeIntrThreshold
+ * empty the FIFO until serviceIntr is set
+ */
+ for (i=ppc->ppc_fifo; i>0; i--) {
+ if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) {
+ LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO");
+ goto error;
+ }
+ if (r_ecr(ppc) & PPC_SERVICE_INTR) {
+ /* writeIntrThreshold reached */
+ ppc->ppc_wthr = ppc->ppc_fifo - i+1;
+ }
+ /* if FIFO empty before the last byte, error */
+ if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO");
+ goto error;
+ }
+ }
+
+ /* FIFO must be empty after the last byte */
+ if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+ LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO");
+ goto error;
+ }
+
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (0);
+
+error:
+ w_ctr(ppc, ctr_sav);
+ w_ecr(ppc, ecr_sav);
+
+ return (EINVAL);
}
static int
@@ -654,6 +767,13 @@ config:
ppc->ppc_avm = chipset_mode;
}
+ /* set FIFO threshold to 16 */
+ if (ppc->ppc_avm & PPB_ECP) {
+ /* select CRA */
+ outb(csr, 0xa);
+ outb(cio, 16);
+ }
+
end_detect:
if (bootverbose)
@@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
if (!chipset_mode) {
/* first, check for ECP */
- w_ecr(ppc, 0x20);
- if ((r_ecr(ppc) & 0xe0) == 0x20) {
+ w_ecr(ppc, PPC_ECR_PS2);
+ if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) {
ppc->ppc_avm |= PPB_ECP | PPB_SPP;
if (bootverbose)
printf(" ECP SPP");
/* search for SMC style ECP+EPP mode */
- w_ecr(ppc, 0x80);
+ w_ecr(ppc, PPC_ECR_EPP);
}
/* try to reset EPP timeout bit */
@@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
}
} else {
/* restore to standard mode */
- w_ecr(ppc, 0x0);
+ w_ecr(ppc, PPC_ECR_STD);
}
/* XXX try to detect NIBBLE and PS2 modes */
@@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) {
}
}
+ /* configure/detect ECP FIFO */
+ if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80))
+ ppc_detect_fifo(ppc);
+
return (0);
}
@@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq)
/* unreached */
}
+static void
+ppcintr(int unit)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ctr, ecr;
+
+ ctr = r_ctr(ppc);
+ ecr = r_ecr(ppc);
+
+#ifdef PPC_DEBUG
+ printf("!");
+#endif
+
+ /* don't use ecp mode with IRQENABLE set */
+ if (ctr & IRQENABLE) {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+
+ if (ctr & nFAULT) {
+ if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) {
+
+ w_ecr(ppc, ecr | PPC_nFAULT_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT;
+ } else {
+ /* call upper code */
+ ppb_intr(&ppc->ppc_link);
+ return;
+ }
+ }
+
+ if (ppc->ppc_irqstat & PPC_IRQ_DMA) {
+ /* disable interrupts (should be done by hardware though) */
+ w_ecr(ppc, ecr | PPC_SERVICE_INTR);
+ ppc->ppc_irqstat &= ~PPC_IRQ_DMA;
+ ecr = r_ecr(ppc);
+
+ /* check if DMA completed */
+ if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) {
+#ifdef PPC_DEBUG
+ printf("a");
+#endif
+ /* stop DMA */
+ w_ecr(ppc, ecr & ~PPC_ENABLE_DMA);
+ ecr = r_ecr(ppc);
+
+ if (ppc->ppc_dmastat == PPC_DMA_STARTED) {
+#ifdef PPC_DEBUG
+ printf("d");
+#endif
+ isa_dmadone(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+
+ ppc->ppc_dmastat = PPC_DMA_COMPLETE;
+
+ /* wakeup the waiting process */
+ wakeup((caddr_t)ppc);
+ }
+ }
+ } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) {
+
+ /* classic interrupt I/O */
+ ppc->ppc_irqstat &= ~PPC_IRQ_FIFO;
+
+ }
+
+ return;
+}
+
+static int
+ppc_read(int unit, char *buf, int len, int mode)
+{
+ return (EINVAL);
+}
+
+/*
+ * Call this function if you want to send data in any advanced mode
+ * of your parallel port: FIFO, DMA
+ *
+ * If what you want is not possible (no ECP, no DMA...),
+ * EINVAL is returned
+ */
+static int
+ppc_write(int unit, char *buf, int len, int how)
+{
+ struct ppc_data *ppc = ppcdata[unit];
+ char ecr, ecr_sav, ctr, ctr_sav;
+ int s, error = 0;
+ int spin;
+
+#ifdef PPC_DEBUG
+ printf("w");
+#endif
+
+ ecr_sav = r_ecr(ppc);
+ ctr_sav = r_ctr(ppc);
+
+ /*
+ * Send buffer with DMA, FIFO and interrupts
+ */
+ if (ppc->ppc_avm & PPB_ECP) {
+
+ if (ppc->ppc_dmachan >= 0) {
+
+ /* byte mode, no intr, no DMA, dir=0, flush fifo
+ */
+ ecr = PPC_ECR_STD | PPC_DISABLE_INTR;
+ w_ecr(ppc, ecr);
+
+ /* disable nAck interrupts */
+ ctr = r_ctr(ppc);
+ ctr &= ~IRQENABLE;
+ w_ctr(ppc, ctr);
+
+ ppc->ppc_dmaflags = 0;
+ ppc->ppc_dmaddr = (caddr_t)buf;
+ ppc->ppc_dmacnt = (u_int)len;
+
+ switch (ppc->ppc_mode) {
+ case PPB_COMPATIBLE:
+ /* compatible mode with FIFO, no intr, DMA, dir=0 */
+ ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ case PPB_ECP:
+ ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA;
+ break;
+ default:
+ error = EINVAL;
+ goto error;
+ }
+
+ w_ecr(ppc, ecr);
+ ecr = r_ecr(ppc);
+
+ /* enter splhigh() not to be preempted
+ * by the dma interrupt, we may miss
+ * the wakeup otherwise
+ */
+ s = splhigh();
+
+ ppc->ppc_dmastat = PPC_DMA_INIT;
+
+ /* enable interrupts */
+ ecr &= ~PPC_SERVICE_INTR;
+ ppc->ppc_irqstat = PPC_IRQ_DMA;
+ w_ecr(ppc, ecr);
+
+ isa_dmastart(
+ ppc->ppc_dmaflags,
+ ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt,
+ ppc->ppc_dmachan);
+#ifdef PPC_DEBUG
+ printf("s%d", ppc->ppc_dmacnt);
+#endif
+ ppc->ppc_dmastat = PPC_DMA_STARTED;
+
+ /* Wait for the DMA completed interrupt. We hope we won't
+ * miss it, otherwise a signal will be necessary to unlock the
+ * process.
+ */
+ do {
+ /* release CPU */
+ error = tsleep((caddr_t)ppc,
+ PPBPRI | PCATCH, "ppcdma", 0);
+
+ } while (error == EWOULDBLOCK);
+
+ splx(s);
+
+ if (error) {
+#ifdef PPC_DEBUG
+ printf("i");
+#endif
+ /* stop DMA */
+ isa_dmadone(
+ ppc->ppc_dmaflags, ppc->ppc_dmaddr,
+ ppc->ppc_dmacnt, ppc->ppc_dmachan);
+
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ goto error;
+ }
+
+ /* wait for an empty fifo */
+ while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
+
+ for (spin=100; spin; spin--)
+ if (r_ecr(ppc) & PPC_FIFO_EMPTY)
+ goto fifo_empty;
+#ifdef PPC_DEBUG
+ printf("Z");
+#endif
+ error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100);
+ if (error != EWOULDBLOCK) {
+#ifdef PPC_DEBUG
+ printf("I");
+#endif
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ ppc->ppc_dmastat = PPC_DMA_INTERRUPTED;
+ error = EINTR;
+ goto error;
+ }
+ }
+
+fifo_empty:
+ /* no dma, no interrupt, flush the fifo */
+ w_ecr(ppc, PPC_ECR_RESET);
+
+ } else
+ error = EINVAL; /* XXX we should FIFO and
+ * interrupts */
+ } else
+ error = EINVAL;
+
+error:
+
+ /* PDRQ must be kept unasserted until nPDACK is
+ * deasserted for a minimum of 350ns (SMC datasheet)
+ *
+ * Consequence may be a FIFO that never empty
+ */
+ DELAY(1);
+
+ w_ecr(ppc, ecr_sav);
+ w_ctr(ppc, ctr_sav);
+
+ return (error);
+}
+
/*
* Configure current operating mode
*/
@@ -1209,32 +1571,34 @@ 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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
- return (EOPNOTSUPP);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ return (EINVAL);
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
@@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode)
return (0);
}
+/*
+ * The ppc driver is free to choose options like FIFO or DMA
+ * if ECP mode is available.
+ *
+ * The 'RAW' option allows the upper drivers to force the ppc mode
+ * even with FIFO, DMA available.
+ */
int
ppc_smclike_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);
- }
+ u_char ecr = 0;
/* check if mode is available */
- if (!(ppc->ppc_avm & mode))
- return (EOPNOTSUPP);
+ if (mode && !(ppc->ppc_avm & mode))
+ return (EINVAL);
/* if ECP mode, configure ecr register */
if (ppc->ppc_avm & PPB_ECP) {
+ /* return to byte mode (keeping direction bit),
+ * no interrupt, no DMA to be able to change to
+ * ECP or EPP mode
+ */
+ w_ecr(ppc, PPC_ECR_RESET);
+ ecr = PPC_DISABLE_INTR;
- /* XXX disable DMA, enable interrupts */
if (mode & PPB_EPP)
/* select EPP mode */
- w_ecr(ppc, 0x80);
- else if (mode & PPB_PS2)
- /* select PS2 mode with ECP */
- w_ecr(ppc, 0x20);
+ ecr |= PPC_ECR_EPP;
else if (mode & PPB_ECP)
/* select ECP mode */
- w_ecr(ppc, 0x60);
+ ecr |= PPC_ECR_ECP;
+ else if (mode & PPB_PS2)
+ /* select PS2 mode with ECP */
+ ecr |= PPC_ECR_PS2;
else
- /* select standard parallel port mode */
- w_ecr(ppc, 0x00);
+ /* select COMPATIBLE/NIBBLE mode */
+ ecr |= PPC_ECR_STD;
+
+ w_ecr(ppc, ecr);
}
ppc->ppc_mode = mode;
-
return (0);
}
@@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp)
if((next_bios_ppc < BIOS_MAX_PPC) &&
(*(BIOS_PORTS+next_bios_ppc) != 0) ) {
dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++);
- printf("ppc: parallel port found at 0x%x\n",
- dvp->id_iobase);
+ if (bootverbose)
+ printf("ppc: parallel port found at 0x%x\n",
+ dvp->id_iobase);
} else
return (0);
}
@@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp)
if (!(dvp->id_flags & 0x20))
ppc->ppc_irq = ffs(dvp->id_irq) - 1;
+ ppc->ppc_dmachan = dvp->id_drq;
+
ppcdata[ppc->ppc_unit] = ppc;
nppc ++;
@@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp)
ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ?
ppc_epp_protocol[ppc->ppc_epp] : "");
+ if (ppc->ppc_fifo)
+ printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n",
+ ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr,
+ ppc->ppc_rthr);
+
isdp->id_ointr = ppcintr;
/*
@@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp)
ppc->ppc_link.ppbus = ppbus;
ppbus->ppb_link = &ppc->ppc_link;
+ if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) {
+
+ /* acquire the DMA channel forever */
+ isa_dma_acquire(ppc->ppc_dmachan);
+ isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */
+ }
+
/*
* Probe the ppbus and attach devices found.
*/
diff --git a/sys/isa/ppcreg.h b/sys/isa/ppcreg.h
index af8c9db..829bfd1 100644
--- a/sys/isa/ppcreg.h
+++ b/sys/isa/ppcreg.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $
+ * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $
*
*/
#ifndef __PPCREG_H
@@ -55,6 +55,34 @@ struct ppc_data {
int ppc_mode; /* chipset current mode */
int ppc_avm; /* chipset available modes */
+#define PPC_IRQ_NONE 0x0
+#define PPC_IRQ_nACK 0x1
+#define PPC_IRQ_DMA 0x2
+#define PPC_IRQ_FIFO 0x4
+#define PPC_IRQ_nFAULT 0x8
+ int ppc_irqstat; /* remind irq settings */
+
+#define PPC_DMA_INIT 0x01
+#define PPC_DMA_STARTED 0x02
+#define PPC_DMA_COMPLETE 0x03
+#define PPC_DMA_INTERRUPTED 0x04
+#define PPC_DMA_ERROR 0x05
+ int ppc_dmastat; /* dma state */
+ int ppc_dmachan; /* dma channel */
+ int ppc_dmaflags; /* dma transfer flags */
+ caddr_t ppc_dmaddr; /* buffer address */
+ u_int ppc_dmacnt; /* count of bytes sent with dma */
+
+#define PPC_PWORD_MASK 0x30
+#define PPC_PWORD_16 0x00
+#define PPC_PWORD_8 0x10
+#define PPC_PWORD_32 0x20
+ char ppc_pword; /* PWord size */
+ short ppc_fifo; /* FIFO threshold */
+
+ short ppc_wthr; /* writeIntrThresold */
+ short ppc_rthr; /* readIntrThresold */
+
#define ppc_base ppc_link.base
#define ppc_epp ppc_link.epp_protocol
#define ppc_irq ppc_link.id_irq
@@ -71,25 +99,44 @@ struct ppc_data {
* Parallel Port Chipset registers.
*/
#define PPC_SPP_DTR 0 /* SPP data register */
+#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */
#define PPC_SPP_STR 1 /* SPP status register */
#define PPC_SPP_CTR 2 /* SPP control register */
#define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */
-#define PPC_ECP_FIFO 0x400 /* ECP fifo register */
+#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */
+#define PPC_ECP_CNFGA 0x400 /* Configuration register A */
+#define PPC_ECP_CNFGB 0x401 /* Configuration register B */
#define PPC_ECP_ECR 0x402 /* ECP extended control register */
+#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */
+#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */
+#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */
+#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */
+#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */
+#define PPC_ECR_STD 0x0
+#define PPC_ECR_PS2 0x20
+#define PPC_ECR_FIFO 0x40
+#define PPC_ECR_ECP 0x60
+#define PPC_ECR_EPP 0x80
+
+#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR)
+#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR)
+
#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_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA))
+#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB))
#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 r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_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)
#define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte)
#define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte)
#define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte)
-#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte)
+#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte)
/*
* Register defines for the PC873xx parts
OpenPOWER on IntegriCloud