patch-2.3.10 linux/drivers/misc/parport_pc.c

Next file: linux/drivers/misc/parport_probe.c
Previous file: linux/drivers/misc/parport_init.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.9/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
@@ -9,6 +9,7 @@
  * based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
  *
  * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
+ * DMA support - Bert De Jonghe <bert@sophis.be>
  * Better EPP probing - Carlos Henrique Bauer <chbauer@acm.org>
  */
 
@@ -46,9 +47,11 @@
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/pci.h>
+#include <linux/sysctl.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/uaccess.h>
 
 #include <linux/parport.h>
 #include <linux/parport_pc.h>
@@ -57,12 +60,138 @@
    than PARPORT_MAX (in <linux/parport.h>).  */
 #define PARPORT_PC_MAX_PORTS  8
 
+/* ECR modes */
+#define ECR_SPP 00
+#define ECR_PS2 01
+#define ECR_PPF 02
+#define ECR_ECP 03
+#define ECR_EPP 04
+#define ECR_VND 05
+#define ECR_TST 06
+#define ECR_CNF 07
+
 static int user_specified __initdata = 0;
 
+/* frob_control, but for ECR */
+static void frob_econtrol (struct parport *pb, unsigned char m,
+			   unsigned char v)
+{
+	outb ((inb (ECONTROL (pb)) & ~m) ^ v, ECONTROL (pb));
+}
+
+#ifdef CONFIG_PARPORT_1284
+/* Safely change the mode bits in the ECR */
+static int change_mode(struct parport *p, int m)
+{
+	const struct parport_pc_private *priv = p->physport->private_data;
+	int ecr = ECONTROL(p);
+	unsigned char oecr;
+	int mode;
+
+	if (!priv->ecr) {
+		printk (KERN_DEBUG "change_mode: but there's no ECR!\n");
+		return 0;
+	}
+
+	/* Bits <7:5> contain the mode. */
+	oecr = inb (ecr);
+	mode = (oecr >> 5) & 0x7;
+	if (mode == m) return 0;
+	if (mode && m)
+		/* We have to go through mode 000 */
+		change_mode (p, ECR_SPP);
+
+	if (m < 2 && !(parport_read_control (p) & 0x20)) {
+		/* This mode resets the FIFO, so we may
+		 * have to wait for it to drain first. */
+		long expire = jiffies + p->physport->cad->timeout;
+		int counter;
+		switch (mode) {
+		case ECR_PPF: /* Parallel Port FIFO mode */
+		case ECR_ECP: /* ECP Parallel Port mode */
+			/* Busy wait for 200us */
+			for (counter = 0; counter < 40; counter++) {
+				if (inb (ECONTROL (p)) & 0x01)
+					break;
+				if (signal_pending (current)) break;
+				udelay (5);
+			}
+
+			/* Poll slowly. */
+			while (!(inb (ECONTROL (p)) & 0x01)) {
+				if (time_after_eq (jiffies, expire))
+					/* The FIFO is stuck. */
+					return -EBUSY;
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout ((HZ + 99) / 100);
+				if (signal_pending (current))
+					break;
+			}
+		}
+	}
+
+	/* Set the mode. */
+	oecr &= ~(7 << 5);
+	oecr |= m << 5;
+	outb (oecr, ecr);
+	return 0;
+}
+
+/* Find FIFO lossage; FIFO is reset */
+static int get_fifo_residue (struct parport *p)
+{
+	int residue;
+	int cnfga;
+	const struct parport_pc_private *priv = p->physport->private_data;
+
+	/* Prevent further data transfer. */
+	parport_frob_control (p,
+			      PARPORT_CONTROL_STROBE,
+			      PARPORT_CONTROL_STROBE);
+
+	/* Adjust for the contents of the FIFO. */
+	for (residue = priv->fifo_depth; ; residue--) {
+		if (inb (ECONTROL (p)) & 0x2)
+				/* Full up. */
+			break;
+
+		outb (0, FIFO (p));
+	}
+
+	printk (KERN_DEBUG "%s: %d PWords were left in FIFO\n", p->name,
+		residue);
+
+	/* Reset the FIFO. */
+	frob_econtrol (p, 0xe0, 0x20);
+	parport_frob_control (p, PARPORT_CONTROL_STROBE, 0);
+
+	/* Now change to config mode and clean up. FIXME */
+	frob_econtrol (p, 0xe0, 0xe0);
+	cnfga = inb (CONFIGA (p));
+	printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
+
+	if (!(cnfga & (1<<2))) {
+		printk (KERN_DEBUG "%s: Accounting for extra byte\n", p->name);
+		residue++;
+	}
+
+	/* Don't care about partial PWords until support is added for
+	 * PWord != 1 byte. */
+
+	/* Back to PS2 mode. */
+	frob_econtrol (p, 0xe0, 0x20);
+
+	return residue;
+}
+
+#endif /* IEEE 1284 support */
+
 /*
  * Clear TIMEOUT BIT in EPP MODE
+ *
+ * This is also used in SPP detection.
  */
-int parport_pc_epp_clear_timeout(struct parport *pb)
+static int clear_epp_timeout(struct parport *pb)
 {
 	unsigned char r;
 
@@ -72,187 +201,719 @@
 	/* To clear timeout some chips require double read */
 	parport_pc_read_status(pb);
 	r = parport_pc_read_status(pb);
-	parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
-	parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+	outb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
+	outb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
 	r = parport_pc_read_status(pb);
 
 	return !(r & 0x01);
 }
 
+/*
+ * Access functions.
+ *
+ * These aren't static because they may be used by the parport_xxx_yyy
+ * macros.  extern __inline__ versions of several of these are in
+ * parport_pc.h.
+ */
+
 static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	parport_generic_irq(irq, (struct parport *) dev_id, regs);
 }
 
-void parport_pc_write_epp(struct parport *p, unsigned char d)
+void parport_pc_write_data(struct parport *p, unsigned char d)
 {
-	outb(d, EPPDATA(p));
+	outb (d, DATA (p));
 }
 
-unsigned char parport_pc_read_epp(struct parport *p)
+unsigned char parport_pc_read_data(struct parport *p)
 {
-	return inb(EPPDATA(p));
+	return inb (DATA (p));
 }
 
-void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
+unsigned char __frob_control (struct parport *p, unsigned char mask,
+			      unsigned char val)
 {
-	outb(d, EPPADDR(p));
+	struct parport_pc_private *priv = p->physport->private_data;
+	unsigned char ctr = priv->ctr;
+	ctr = (ctr & ~mask) ^ val;
+	ctr &= priv->ctr_writable; /* only write writable bits. */
+	outb (ctr, CONTROL (p));
+	return priv->ctr = ctr; /* update soft copy */
 }
 
-unsigned char parport_pc_read_epp_addr(struct parport *p)
+void parport_pc_write_control(struct parport *p, unsigned char d)
 {
-	return inb(EPPADDR(p));
-}
+	const unsigned char wm = (PARPORT_CONTROL_STROBE |
+				  PARPORT_CONTROL_AUTOFD |
+				  PARPORT_CONTROL_INIT |
+				  PARPORT_CONTROL_SELECT);
+
+	/* Take this out when drivers have adapted to the newer interface. */
+	if (d & 0x20) {
+			printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+					p->name, p->cad->name);
+			parport_pc_data_reverse (p);
+	}
 
-int parport_pc_check_epp_timeout(struct parport *p)
-{
-	if (!(inb(STATUS(p)) & 1))
-		return 0;
-	parport_pc_epp_clear_timeout(p);
-	return 1;
+	__frob_control (p, wm, d & wm);
 }
 
-unsigned char parport_pc_read_configb(struct parport *p)
+unsigned char parport_pc_read_control(struct parport *p)
 {
-	return inb(CONFIGB(p));
+	const struct parport_pc_private *priv = p->physport->private_data;
+	return priv->ctr; /* Use soft copy */
 }
 
-void parport_pc_write_data(struct parport *p, unsigned char d)
+unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask,
+				       unsigned char val)
 {
-	outb(d, DATA(p));
-}
+	const unsigned char wm = (PARPORT_CONTROL_STROBE |
+				  PARPORT_CONTROL_AUTOFD |
+				  PARPORT_CONTROL_INIT |
+				  PARPORT_CONTROL_SELECT);
+
+	/* Take this out when drivers have adapted to the newer interface. */
+	if (mask & 0x20) {
+			printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+					p->name, p->cad->name);
+			parport_pc_data_reverse (p);
+	}
 
-unsigned char parport_pc_read_data(struct parport *p)
-{
-	return inb(DATA(p));
+	/* Restrict mask and val to control lines. */
+	mask &= wm;
+	val &= wm;
+
+	return __frob_control (p, mask, val);
 }
 
-void parport_pc_write_control(struct parport *p, unsigned char d)
+unsigned char parport_pc_read_status(struct parport *p)
 {
-	struct parport_pc_private *priv = p->private_data;
-	priv->ctr = d;/* update soft copy */
-	outb(d, CONTROL(p));
+	return inb (STATUS (p));
 }
 
-unsigned char parport_pc_read_control(struct parport *p)
+void parport_pc_disable_irq(struct parport *p)
 {
-	struct parport_pc_private *priv = p->private_data;
-	return priv->ctr;
+	__frob_control (p, 0x10, 0);
 }
 
-unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask,  unsigned char val)
+void parport_pc_enable_irq(struct parport *p)
 {
-	struct parport_pc_private *priv = p->private_data;
-	unsigned char ctr = priv->ctr;
-	ctr = (ctr & ~mask) ^ val;
-	outb (ctr, CONTROL(p));
-	return priv->ctr = ctr; /* update soft copy */
+	__frob_control (p, 0x10, 0x10);
 }
 
-void parport_pc_write_status(struct parport *p, unsigned char d)
+void parport_pc_data_forward (struct parport *p)
 {
-	outb(d, STATUS(p));
+	__frob_control (p, 0x20, 0);
 }
 
-unsigned char parport_pc_read_status(struct parport *p)
+void parport_pc_data_reverse (struct parport *p)
 {
-	return inb(STATUS(p));
+	__frob_control (p, 0x20, 0x20);
 }
 
-void parport_pc_write_econtrol(struct parport *p, unsigned char d)
+void parport_pc_init_state(struct pardevice *dev, struct parport_state *s)
 {
-	outb(d, ECONTROL(p));
+	struct parport_pc_private *priv = dev->port->physport->private_data;
+	priv->ctr = s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+	s->u.pc.ecr = 0x24;
 }
 
-unsigned char parport_pc_read_econtrol(struct parport *p)
+void parport_pc_save_state(struct parport *p, struct parport_state *s)
 {
-	return inb(ECONTROL(p));
+	const struct parport_pc_private *priv = p->physport->private_data;
+	s->u.pc.ctr = inb (CONTROL (p));
+	if (priv->ecr)
+		s->u.pc.ecr = inb (ECONTROL (p));
 }
 
-unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask,  unsigned char val)
+void parport_pc_restore_state(struct parport *p, struct parport_state *s)
 {
-	unsigned char old = inb(ECONTROL(p));
-	outb(((old & ~mask) ^ val), ECONTROL(p));
-	return old;
+	const struct parport_pc_private *priv = p->physport->private_data;
+	outb (s->u.pc.ctr, CONTROL (p));
+	if (priv->ecr)
+		outb (s->u.pc.ecr, ECONTROL (p));
 }
 
-void parport_pc_change_mode(struct parport *p, int m)
+#ifdef CONFIG_PARPORT_1284
+static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
+					size_t length, int flags)
 {
-	/* FIXME */
+	size_t got = 0;
+	for (; got < length; got++) {
+		*((char*)buf)++ = inb (EPPDATA(port));
+		if (inb (STATUS(port)) & 0x01) {
+			clear_epp_timeout (port);
+			break;
+		}
+	}
+
+	return got;
 }
 
-void parport_pc_write_fifo(struct parport *p, unsigned char v)
+static size_t parport_pc_epp_write_data (struct parport *port, const void *buf,
+					 size_t length, int flags)
 {
-	outb (v, CONFIGA(p));
+	size_t written = 0;
+	for (; written < length; written++) {
+		outb (*((char*)buf)++, EPPDATA(port));
+		if (inb (STATUS(port)) & 0x01) {
+			clear_epp_timeout (port);
+			break;
+		}
+	}
+
+	return written;
 }
 
-unsigned char parport_pc_read_fifo(struct parport *p)
+static size_t parport_pc_epp_read_addr (struct parport *port, void *buf,
+					size_t length, int flags)
 {
-	return inb (CONFIGA(p));
+	size_t got = 0;
+	for (; got < length; got++) {
+		*((char*)buf)++ = inb (EPPADDR (port));
+		if (inb (STATUS (port)) & 0x01) {
+			clear_epp_timeout (port);
+			break;
+		}
+	}
+
+	return got;
 }
 
-void parport_pc_disable_irq(struct parport *p)
+static size_t parport_pc_epp_write_addr (struct parport *port,
+					 const void *buf, size_t length,
+					 int flags)
 {
-	parport_pc_frob_control(p, 0x10, 0);
+	size_t written = 0;
+	for (; written < length; written++) {
+		outb (*((char*)buf)++, EPPADDR (port));
+		if (inb (STATUS (port)) & 0x01) {
+			clear_epp_timeout (port);
+			break;
+		}
+	}
+
+	return written;
 }
 
-void parport_pc_enable_irq(struct parport *p)
+static size_t parport_pc_ecpepp_read_data (struct parport *port, void *buf,
+					   size_t length, int flags)
 {
-	parport_pc_frob_control(p, 0x10, 0x10);
+	size_t got;
+
+	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	got = parport_pc_epp_read_data (port, buf, length, flags);
+	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+	return got;
 }
 
-void parport_pc_init_state(struct parport_state *s)
-{
-	s->u.pc.ctr = 0xc;
-	s->u.pc.ecr = 0x0;
+static size_t parport_pc_ecpepp_write_data (struct parport *port,
+					    const void *buf, size_t length,
+					    int flags)
+{
+	size_t written;
+
+	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	written = parport_pc_epp_write_data (port, buf, length, flags);
+	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+	return written;
 }
 
-void parport_pc_save_state(struct parport *p, struct parport_state *s)
+static size_t parport_pc_ecpepp_read_addr (struct parport *port, void *buf,
+					   size_t length, int flags)
 {
-	s->u.pc.ctr = parport_pc_read_control(p);
-	if (p->modes & PARPORT_MODE_PCECR)
-		s->u.pc.ecr = parport_pc_read_econtrol(p);
+	size_t got;
+
+	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	got = parport_pc_epp_read_addr (port, buf, length, flags);
+	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+	return got;
 }
 
-void parport_pc_restore_state(struct parport *p, struct parport_state *s)
-{
-	parport_pc_write_control(p, s->u.pc.ctr);
-	if (p->modes & PARPORT_MODE_PCECR)
-		parport_pc_write_econtrol(p, s->u.pc.ecr);
+static size_t parport_pc_ecpepp_write_addr (struct parport *port,
+					    const void *buf, size_t length,
+					    int flags)
+{
+	size_t written;
+
+	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	written = parport_pc_epp_write_addr (port, buf, length, flags);
+	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+	return written;
 }
+#endif /* IEEE 1284 support */
 
-size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
-{
-	size_t got = 0;
-	for (; got < length; got++) {
-		*((char*)buf)++ = inb (EPPDATA(p));
-		if (inb (STATUS(p)) & 0x01)
+#ifdef CONFIG_PARPORT_PC_FIFO
+static size_t parport_pc_fifo_write_block_pio (struct parport *port,
+					       const void *buf, size_t length)
+{
+	int ret = 0;
+	const unsigned char *bufp = buf;
+	size_t left = length;
+	long expire = jiffies + port->physport->cad->timeout;
+	const int fifo = FIFO (port);
+	int poll_for = 8; /* 80 usecs */
+	const struct parport_pc_private *priv = port->physport->private_data;
+	const int fifo_depth = priv->fifo_depth;
+
+	port = port->physport;
+
+	/* We don't want to be interrupted every character. */
+	parport_pc_disable_irq (port);
+	frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
+
+	/* Forward mode. */
+	parport_pc_data_forward (port);
+
+	while (left) {
+		unsigned char byte;
+		unsigned char ecrval = inb (ECONTROL (port));
+		int i = 0;
+
+		if (current->need_resched && time_before (jiffies, expire))
+			/* Can't yield the port. */
+			schedule ();
+
+		/* Anyone else waiting for the port? */
+		if (port->waithead) {
+			printk (KERN_DEBUG "Somebody wants the port\n");
 			break;
-	}
-	return got;
+		}
+
+		if (ecrval & 0x02) {
+			/* FIFO is full. Wait for interrupt. */
+
+			/* Clear serviceIntr */
+			outb (ecrval & ~(1<<2), ECONTROL (port));
+		false_alarm:
+			ret = parport_wait_event (port, HZ);
+			if (ret < 0) break;
+			ret = 0;
+			if (!time_before (jiffies, expire)) {
+				/* Timed out. */
+				printk (KERN_DEBUG "Timed out\n");
+				break;
+			}
+			ecrval = inb (ECONTROL (port));
+			if (!(ecrval & (1<<2))) {
+				if (current->need_resched &&
+				    time_before (jiffies, expire))
+					schedule ();
+
+				goto false_alarm;
+			}
+
+			continue;
+		}
+
+		/* Can't fail now. */
+		expire = jiffies + port->cad->timeout;
+
+	poll:
+		if (signal_pending (current))
+			break;
+
+		if (ecrval & 0x01) {
+			/* FIFO is empty. Blast it full. */
+			const int n = left < fifo_depth ? left : fifo_depth;
+			outsb (fifo, bufp, n);
+			bufp += n;
+			left -= n;
+
+			/* Adjust the poll time. */
+			if (i < (poll_for - 2)) poll_for--;
+			continue;
+		} else if (i++ < poll_for) {
+			udelay (10);
+			ecrval = inb (ECONTROL (port));
+			goto poll;
+		}
+
+		/* Half-full (call me an optimist) */
+		byte = *bufp++;
+		outb (byte, fifo);
+		left--;
+        }
+
+	return length - left;
 }
 
-size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length)
+static size_t parport_pc_fifo_write_block_dma (struct parport *port,
+					       const void *buf, size_t length)
 {
-	size_t written = 0;
-	for (; written < length; written++) {
-		outb (*((char*)buf)++, EPPDATA(p));
-		if (inb (STATUS(p)) & 0x01)
+	int ret = 0;
+	unsigned long dmaflag;
+	size_t left  = length;
+	const struct parport_pc_private *priv = port->physport->private_data;
+
+	port = port->physport;
+
+	/* We don't want to be interrupted every character. */
+	parport_pc_disable_irq (port);
+	frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
+
+	/* Forward mode. */
+	parport_pc_data_forward (port);
+
+	while (left) {
+		long expire = jiffies + port->physport->cad->timeout;
+
+		size_t count = left;
+
+		if (count > PAGE_SIZE)
+			count = PAGE_SIZE;
+
+		memcpy(priv->dma_buf, buf, count);
+
+		dmaflag = claim_dma_lock();
+		disable_dma(port->dma);
+		clear_dma_ff(port->dma);
+		set_dma_mode(port->dma, DMA_MODE_WRITE);
+		set_dma_addr(port->dma, virt_to_bus((volatile char *) priv->dma_buf));
+		set_dma_count(port->dma, count);
+
+		/* Set DMA mode */
+		frob_econtrol (port, 1<<3, 1<<3);
+
+		/* Clear serviceIntr */
+		frob_econtrol (port, 1<<2, 0);
+
+		enable_dma(port->dma);
+		release_dma_lock(dmaflag);
+
+		/* assume DMA will be successful */
+		left -= count;
+		buf  += count;
+
+		/* Wait for interrupt. */
+	false_alarm:
+		ret = parport_wait_event (port, HZ);
+		if (ret < 0) break;
+		ret = 0;
+		if (!time_before (jiffies, expire)) {
+			/* Timed out. */
+			printk (KERN_DEBUG "Timed out\n");
+			break;
+		}
+		/* Is serviceIntr set? */
+		if (!(inb (ECONTROL (port)) & (1<<2))) {
+			if (current->need_resched)
+				schedule ();
+
+			goto false_alarm;
+		}
+
+		dmaflag = claim_dma_lock();
+		disable_dma(port->dma);
+		clear_dma_ff(port->dma);
+		count = get_dma_residue(port->dma);
+		release_dma_lock(dmaflag);
+
+		if (current->need_resched)
+			/* Can't yield the port. */
+			schedule ();
+
+		/* Anyone else waiting for the port? */
+		if (port->waithead) {
+			printk (KERN_DEBUG "Somebody wants the port\n");
 			break;
+		}
+
+		/* update for possible DMA residue ! */
+		buf  -= count;
+		left += count;
 	}
+
+	/* Maybe got here through break, so adjust for DMA residue! */
+	dmaflag = claim_dma_lock();
+	disable_dma(port->dma);
+	clear_dma_ff(port->dma);
+	left += get_dma_residue(port->dma);
+	release_dma_lock(dmaflag);
+
+	/* Turn off DMA mode */
+	frob_econtrol (port, 1<<3, 0);
+
+	return length - left;
+}
+
+/* Parallel Port FIFO mode (ECP chipsets) */
+size_t parport_pc_compat_write_block_pio (struct parport *port,
+					  const void *buf, size_t length,
+					  int flags)
+{
+	size_t written;
+
+	/* Special case: a timeout of zero means we cannot call schedule(). */
+	if (!port->physport->cad->timeout)
+		return parport_ieee1284_write_compat (port, buf,
+						      length, flags);
+
+	/* Set up parallel port FIFO mode.*/
+	change_mode (port, ECR_PPF); /* Parallel port FIFO */
+	parport_pc_data_forward (port);
+	port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+	/* Write the data to the FIFO. */
+	if (port->dma != PARPORT_DMA_NONE)
+		written = parport_pc_fifo_write_block_dma (port, buf, length);
+	else
+		written = parport_pc_fifo_write_block_pio (port, buf, length);
+
+	/* Finish up. */
+	if (change_mode (port, ECR_PS2) == -EBUSY) {
+		const struct parport_pc_private *priv = 
+			port->physport->private_data;
+
+		printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+
+		/* Prevent further data transfer. */
+		parport_frob_control (port,
+				      PARPORT_CONTROL_STROBE,
+				      PARPORT_CONTROL_STROBE);
+
+		/* Adjust for the contents of the FIFO. */
+		for (written -= priv->fifo_depth; ; written++) {
+			if (inb (ECONTROL (port)) & 0x2)
+				/* Full up. */
+				break;
+
+			outb (0, FIFO (port));
+		}
+
+		/* Reset the FIFO. */
+		frob_econtrol (port, 0xe0, 0);
+
+		/* De-assert strobe. */
+		parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+	}
+
+	parport_wait_peripheral (port,
+				 PARPORT_STATUS_BUSY,
+				 PARPORT_STATUS_BUSY);
+	port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
 	return written;
 }
 
-int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
-{
-	return -ENOSYS; /* FIXME */
+/* ECP */
+#ifdef CONFIG_PARPORT_1284
+size_t parport_pc_ecp_write_block_pio (struct parport *port,
+				       const void *buf, size_t length,
+				       int flags)
+{
+	size_t written;
+
+	/* Special case: a timeout of zero means we cannot call schedule(). */
+	if (!port->physport->cad->timeout)
+		return parport_ieee1284_ecp_write_data (port, buf,
+							length, flags);
+
+	/* Switch to forward mode if necessary. */
+	if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
+		/* Event 47: Set nInit high. */
+		parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+
+		/* Event 40: PError goes high. */
+		parport_wait_peripheral (port,
+					 PARPORT_STATUS_PAPEROUT,
+					 PARPORT_STATUS_PAPEROUT);
+	}
+
+	/* Set up ECP parallel port mode.*/
+	change_mode (port, ECR_ECP); /* ECP FIFO */
+	parport_pc_data_forward (port);
+	port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+	/* Write the data to the FIFO. */
+	if (port->dma != PARPORT_DMA_NONE)
+		written = parport_pc_fifo_write_block_dma (port, buf, length);
+	else
+		written = parport_pc_fifo_write_block_pio (port, buf, length);
+
+	/* Finish up. */
+	if (change_mode (port, ECR_PS2) == -EBUSY) {
+		const struct parport_pc_private *priv =
+			port->physport->private_data;
+
+		printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
+
+		/* Prevent further data transfer. */
+		parport_frob_control (port,
+				      PARPORT_CONTROL_STROBE,
+				      PARPORT_CONTROL_STROBE);
+
+		/* Adjust for the contents of the FIFO. */
+		for (written -= priv->fifo_depth; ; written++) {
+			if (inb (ECONTROL (port)) & 0x2)
+				/* Full up. */
+				break;
+
+			outb (0, FIFO (port));
+		}
+
+		/* Reset the FIFO. */
+		frob_econtrol (port, 0xe0, 0);
+		parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
+
+		/* Host transfer recovery. */
+		parport_frob_control (port,
+				      PARPORT_CONTROL_INIT,
+				      PARPORT_CONTROL_INIT);
+		parport_pc_data_reverse (port);
+		parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
+		parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+		parport_wait_peripheral (port,
+					 PARPORT_STATUS_PAPEROUT,
+					 PARPORT_STATUS_PAPEROUT);
+	}
+
+	parport_wait_peripheral (port,
+				 PARPORT_STATUS_BUSY, 
+				 PARPORT_STATUS_BUSY);
+	port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+	return written;
 }
 
-int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
+size_t parport_pc_ecp_read_block_pio (struct parport *port,
+				      void *buf, size_t length, int flags)
 {
-	return -ENOSYS; /* FIXME */
+	size_t left = length;
+	size_t fifofull;
+	const int fifo = FIFO(port);
+	const struct parport_pc_private *priv = port->physport->private_data;
+	const int fifo_depth = priv->fifo_depth;
+	char *bufp = buf;
+
+	port = port->physport;
+
+	/* Special case: a timeout of zero means we cannot call schedule(). */
+	if (!port->cad->timeout)
+		return parport_ieee1284_ecp_read_data (port, buf,
+						       length, flags);
+
+	fifofull = fifo_depth;
+	if (port->ieee1284.mode == IEEE1284_MODE_ECPRLE)
+		/* If the peripheral is allowed to send RLE compressed
+		 * data, it is possible for a byte to expand to 128
+		 * bytes in the FIFO. */
+		fifofull = 128;
+
+	/* If the caller wants less than a full FIFO's worth of data,
+	 * go through software emulation.  Otherwise we may have to through
+	 * away data. */
+	if (length < fifofull)
+		return parport_ieee1284_ecp_read_data (port, buf,
+						       length, flags);
+
+	/* Switch to reverse mode if necessary. */
+	if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) {
+		/* Event 38: Set nAutoFd low */
+		parport_frob_control (port,
+				      PARPORT_CONTROL_AUTOFD,
+				      PARPORT_CONTROL_AUTOFD);
+		parport_pc_data_reverse (port);
+		udelay (5);
+
+		/* Event 39: Set nInit low to initiate bus reversal */
+		parport_frob_control (port,
+				      PARPORT_CONTROL_INIT,
+				      PARPORT_CONTROL_INIT);
+
+		/* Event 40: PError goes low */
+		parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
+	}
+
+	/* Set up ECP parallel port mode.*/
+	change_mode (port, ECR_ECP); /* ECP FIFO */
+	parport_pc_data_reverse (port);
+	port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+
+	/* Do the transfer. */
+	while (left > fifofull) {
+		int ret;
+		long int expire = jiffies + port->cad->timeout;
+		unsigned char ecrval = inb (ECONTROL (port));
+
+		if (current->need_resched && time_before (jiffies, expire))
+			/* Can't yield the port. */
+			schedule ();
+
+		/* At this point, the FIFO may already be full.
+		 * Ideally, we'd be able to tell the port to hold on
+		 * for a second while we empty the FIFO, and we'd be
+		 * able to ensure that no data is lost.  I'm not sure
+		 * that's the case. :-(  It might be that you can play
+		 * games with STB, as in the forward case; someone should
+		 * look at a datasheet. */
+
+		if (ecrval & 0x01) {
+			/* FIFO is empty. Wait for interrupt. */
+
+			/* Anyone else waiting for the port? */
+			if (port->waithead) {
+				printk (KERN_DEBUG
+					"Somebody wants the port\n");
+				break;
+			}
+
+			/* Clear serviceIntr */
+			outb (ecrval & ~(1<<2), ECONTROL (port));
+		false_alarm:
+			ret = parport_wait_event (port, HZ);
+			if (ret < 0) break;
+			ret = 0;
+			if (!time_before (jiffies, expire)) {
+				/* Timed out. */
+				printk (KERN_DEBUG "Timed out\n");
+				break;
+			}
+			ecrval = inb (ECONTROL (port));
+			if (!(ecrval & (1<<2))) {
+				if (current->need_resched &&
+				    time_before (jiffies, expire))
+					schedule ();
+
+				goto false_alarm;
+			}
+
+			continue;
+		}
+
+		if (ecrval & 0x02) {
+			/* FIFO is full. */
+			insb (fifo, bufp, fifo_depth);
+			bufp += fifo_depth;
+			left -= fifo_depth;
+			continue;
+		}
+
+		*bufp++ = inb (fifo);
+		left--;
+	}
+
+	/* Finish up. */
+	if (change_mode (port, ECR_PS2) == -EBUSY) {
+		int lost = get_fifo_residue (port);
+		printk (KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n", port->name,
+			lost);
+	}
+
+	port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+
+	return length - left;
 }
 
+#endif /* IEEE 1284 support */
+
+#endif /* Allowed to use FIFO/DMA */
+
 void parport_pc_inc_use_count(void)
 {
 #ifdef MODULE
@@ -269,6 +930,7 @@
 
 static void parport_pc_fill_inode(struct inode *inode, int fill)
 {
+	/* Is this still needed? -tim */
 #ifdef MODULE
 	if (fill)
 		MOD_INC_USE_COUNT;
@@ -286,46 +948,39 @@
 	parport_pc_read_control,
 	parport_pc_frob_control,
 
-	parport_pc_write_econtrol,
-	parport_pc_read_econtrol,
-	parport_pc_frob_econtrol,
-
-	parport_pc_write_status,
 	parport_pc_read_status,
 
-	parport_pc_write_fifo,
-	parport_pc_read_fifo,
-	
-	parport_pc_change_mode,
-	
-	parport_pc_write_epp,
-	parport_pc_read_epp,
-	parport_pc_write_epp_addr,
-	parport_pc_read_epp_addr,
-	parport_pc_check_epp_timeout,
+	parport_pc_enable_irq,
+	parport_pc_disable_irq,
 
-	parport_pc_epp_write_block,
-	parport_pc_epp_read_block,
+	parport_pc_data_forward,
+	parport_pc_data_reverse,
 
-	parport_pc_ecp_write_block,
-	parport_pc_ecp_read_block,
-	
+	parport_pc_interrupt,
 	parport_pc_init_state,
 	parport_pc_save_state,
 	parport_pc_restore_state,
 
-	parport_pc_enable_irq,
-	parport_pc_disable_irq,
-	parport_pc_interrupt,
-
 	parport_pc_inc_use_count,
 	parport_pc_dec_use_count,
-	parport_pc_fill_inode
+	parport_pc_fill_inode,
+
+	parport_ieee1284_epp_write_data,
+	parport_ieee1284_epp_read_data,
+	parport_ieee1284_epp_write_addr,
+	parport_ieee1284_epp_read_addr,
+
+	parport_ieee1284_ecp_write_data,
+	parport_ieee1284_ecp_read_data,
+	parport_ieee1284_ecp_write_addr,
+
+	parport_ieee1284_write_compat,
+	parport_ieee1284_read_nibble,
+	parport_ieee1284_read_byte,
 };
 
 /* --- Mode detection ------------------------------------- */
 
-
 /*
  * Checks for port existence, all ports support SPP MODE
  */
@@ -339,22 +994,23 @@
 	 * that does not even respond to SPP cycles if an EPP
 	 * timeout is pending
 	 */
-	parport_pc_epp_clear_timeout(pb);
+	clear_epp_timeout(pb);
 
 	/* Do a simple read-write test to make sure the port exists. */
 	w = 0xc;
-	parport_pc_write_control(pb, w);
+	outb (w, CONTROL (pb));
 
-	/* Can we read from the control register?  Some ports don't
-	 * allow reads, so read_control just returns a software
-	 * copy. Some ports _do_ allow reads, so bypass the software
-	 * copy here.  In addition, some bits aren't writable. */
+	/* Is there a control register that we can read from?  Some
+	 * ports don't allow reads, so read_control just returns a
+	 * software copy. Some ports _do_ allow reads, so bypass the
+	 * software copy here.  In addition, some bits aren't
+	 * writable. */
 	r = inb (CONTROL (pb));
 	if ((r & 0xf) == w) {
 		w = 0xe;
-		parport_pc_write_control (pb, w);
-		r = inb (CONTROL(pb));
-		parport_pc_write_control (pb, 0xc);
+		outb (w, CONTROL (pb));
+		r = inb (CONTROL (pb));
+		outb (0xc, CONTROL (pb));
 		if ((r & 0xf) == w)
 			return PARPORT_MODE_PCSPP;
 	}
@@ -379,20 +1035,20 @@
 	}
 
 	if (user_specified)
-		/* Didn't work with 0xaa, but the user is convinced
-		 * this is the place. */
+		/* Didn't work, but the user is convinced this is the
+		 * place. */
 		printk (KERN_DEBUG "0x%lx: DATA: wrote 0x%02x, read 0x%02x\n",
 			pb->base, w, r);
 
 	/* It's possible that we can't read the control register or
-	   the data register.  In that case just believe the user. */
+	 * the data register.  In that case just believe the user. */
 	if (user_specified)
 		return PARPORT_MODE_PCSPP;
 
 	return 0;
 }
 
-/* Check for ECP
+/* Check for ECR
  *
  * Old style XT ports alias io ports every 0x400, hence accessing ECR
  * on these cards actually accesses the CTR.
@@ -401,82 +1057,225 @@
  * regardless of what is written here if the card does NOT support
  * ECP.
  *
- * We will write 0x2c to ECR and 0xcc to CTR since both of these
- * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ * We first check to see if ECR is the same as CTR.  If not, the low
+ * two bits of ECR aren't writable, so we check by writing ECR and
+ * reading it back to see if it's what we expect.
  */
 static int __init parport_ECR_present(struct parport *pb)
 {
-	unsigned char r;
+	struct parport_pc_private *priv = pb->private_data;
+	unsigned char r = 0xc;
 
-	parport_pc_write_control (pb, 0xc);
-	r = parport_pc_read_control(pb);	
-	if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) {
-		parport_pc_write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
+	priv->ecr = 0;
+	outb (r, CONTROL (pb));
+	if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) {
+		outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */
 
-		r = parport_pc_read_control(pb);	
-		if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2))
+		r = inb (CONTROL (pb));
+		if ((inb (ECONTROL (pb)) & 0x2) == (r & 0x2))
 			goto no_reg; /* Sure that no ECR register exists */
 	}
 	
-	if ((parport_pc_read_econtrol(pb) & 0x3 ) != 0x1)
+	if ((inb (ECONTROL (pb)) & 0x3 ) != 0x1)
 		goto no_reg;
 
-	parport_pc_write_econtrol(pb, 0x34);
-	if (parport_pc_read_econtrol(pb) != 0x35)
+	outb (0x34, ECONTROL (pb));
+	if (inb (ECONTROL (pb)) != 0x35)
 		goto no_reg;
 
-	parport_pc_write_control(pb, 0xc);
-
-	/* Go to mode 000; SPP, reset FIFO */
-	parport_pc_frob_econtrol (pb, 0xe0, 0x00);
+	priv->ecr = 1;
+	outb (0xc, CONTROL (pb));
 	
-	return PARPORT_MODE_PCECR;
+	/* Go to mode 000 */
+	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+
+	return 1;
 
  no_reg:
-	parport_pc_write_control (pb, 0xc);
-	return 0;
+	outb (0xc, CONTROL (pb));
+	return 0; 
+}
+
+#ifdef CONFIG_PARPORT_1284
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines.  In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero.  So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present. 
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal.  Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic. 
+ */
+
+static int __init parport_PS2_supported(struct parport *pb)
+{
+	int ok = 0;
+  
+	clear_epp_timeout(pb);
+
+	/* try to tri-state the buffer */
+	parport_pc_data_reverse (pb);
+	
+	parport_pc_write_data(pb, 0x55);
+	if (parport_pc_read_data(pb) != 0x55) ok++;
+
+	parport_pc_write_data(pb, 0xaa);
+	if (parport_pc_read_data(pb) != 0xaa) ok++;
+
+	/* cancel input mode */
+	parport_pc_data_forward (pb);
+
+	if (ok)
+		pb->modes |= PARPORT_MODE_TRISTATE;
+	else {
+		struct parport_pc_private *priv = pb->private_data;
+		priv->ctr_writable &= ~0x20;
+	}
+
+	return ok;
 }
 
 static int __init parport_ECP_supported(struct parport *pb)
 {
 	int i;
-	unsigned char oecr;
-	
+	int config;
+	int pword;
+	struct parport_pc_private *priv = pb->private_data;
+
 	/* If there is no ECR, we have no hope of supporting ECP. */
-	if (!(pb->modes & PARPORT_MODE_PCECR))
+	if (!priv->ecr)
 		return 0;
 
-	oecr = parport_pc_read_econtrol(pb);
+	/* Find out FIFO depth */
+	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+	outb (ECR_TST << 5, ECONTROL (pb)); /* TEST FIFO */
+	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
+		outb (0xaa, FIFO (pb));
+
 	/*
 	 * Using LGS chipset it uses ECR register, but
 	 * it doesn't support ECP or FIFO MODE
 	 */
+	if (i == 1024) {
+		outb (ECR_SPP << 5, ECONTROL (pb));
+		return 0;
+	}
+
+	priv->fifo_depth = i;
+	printk (KERN_INFO "0x%lx: FIFO is %d bytes\n", pb->base, i);
+
+	/* Find out writeIntrThreshold */
+	frob_econtrol (pb, 1<<2, 1<<2);
+	frob_econtrol (pb, 1<<2, 0);
+	for (i = 1; i <= priv->fifo_depth; i++) {
+		inb (FIFO (pb));
+		udelay (50);
+		if (inb (ECONTROL (pb)) & (1<<2))
+			break;
+	}
+
+	if (i <= priv->fifo_depth)
+		printk (KERN_INFO "0x%lx: writeIntrThreshold is %d\n",
+			pb->base, i);
+	else
+		/* Number of bytes we know we can write if we get an
+                   interrupt. */
+		i = 0;
+
+	priv->writeIntrThreshold = i;
+
+	/* Find out readIntrThreshold */
+	frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO */
+	parport_pc_data_reverse (pb);
+	frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */
+	frob_econtrol (pb, 1<<2, 1<<2);
+	frob_econtrol (pb, 1<<2, 0);
+	for (i = 1; i <= priv->fifo_depth; i++) {
+		outb (0xaa, FIFO (pb));
+		if (inb (ECONTROL (pb)) & (1<<2))
+			break;
+	}
+
+	if (i <= priv->fifo_depth)
+		printk (KERN_INFO "0x%lx: readIntrThreshold is %d\n",
+			pb->base, i);
+	else
+		/* Number of bytes we can read if we get an interrupt. */
+		i = 0;
+
+	priv->readIntrThreshold = i;
+
+	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+	outb (0xf4, ECONTROL (pb)); /* Configuration mode */
+	config = inb (FIFO (pb));
+	pword = (config >> 4) & 0x7;
+	switch (pword) {
+	case 0:
+		pword = 2;
+		printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+			pb->base);
+		break;
+	case 2:
+		pword = 4;
+		printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
+			pb->base);
+		break;
+	default:
+		printk (KERN_WARNING "0x%lx: Unknown implementation ID\n",
+			pb->base);
+		/* Assume 1 */
+	case 1:
+		pword = 1;
+	}
+	priv->pword = pword;
+	printk (KERN_DEBUG "0x%lx: PWord is %d bits\n", pb->base, 8 * pword);
+
+	config = inb (CONFIGB (pb));
+	printk (KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
+		config & 0x80 ? "Level" : "Pulses");
+
+	if (!(config & 0x40)) {
+		printk (KERN_WARNING "0x%lx: IRQ conflict!\n", pb->base);
+		pb->irq = PARPORT_IRQ_NONE;
+	}
+
+	/* Go back to mode 000 */
+	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+	pb->modes |= PARPORT_MODE_ECP;
+
+	return 1;
+}
+
+static int __init parport_ECPPS2_supported(struct parport *pb)
+{
+	const struct parport_pc_private *priv = pb->private_data;
+	int result;
+	unsigned char oecr;
+
+	if (!priv->ecr)
+		return 0;
+
+	oecr = inb (ECONTROL (pb));
+	outb (ECR_PS2 << 5, ECONTROL (pb));
 	
-	parport_pc_write_econtrol(pb, 0xc0); /* TEST FIFO */
-	for (i=0; i < 1024 && (parport_pc_read_econtrol(pb) & 0x01); i++)
-		parport_pc_write_fifo(pb, 0xaa);
-
-	parport_pc_write_econtrol(pb, oecr);
-	return (i==1024)?0:PARPORT_MODE_PCECP;
+	result = parport_PS2_supported(pb);
+
+	outb (oecr, ECONTROL (pb));
+	return result;
 }
 
-/* EPP mode detection
- * Theory:
- *	Bit 0 of STR is the EPP timeout bit, this bit is 0
- *	when EPP is possible and is set high when an EPP timeout
- *	occurs (EPP uses the HALT line to stop the CPU while it does
- *	the byte transfer, an EPP timeout occurs if the attached
- *	device fails to respond after 10 micro seconds).
- *
- *	This bit is cleared by either reading it (National Semi)
- *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
- *	This bit is always high in non EPP modes.
- */
+/* EPP mode detection  */
+
 static int __init parport_EPP_supported(struct parport *pb)
 {
-	/* If EPP timeout bit clear then EPP available */
-	if (!parport_pc_epp_clear_timeout(pb))
-		return 0;  /* No way to clear timeout */
+	const struct parport_pc_private *priv = pb->private_data;
 
 	/*
 	 * Theory:
@@ -491,132 +1290,70 @@
 	 *	This bit is always high in non EPP modes.
 	 */
 
-	parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x20);
-	parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x10);
-	parport_pc_epp_clear_timeout(pb);
-	
-	parport_pc_read_epp(pb);
-	udelay(30);  /* Wait for possible EPP timeout */
-	
-	if (parport_pc_read_status(pb) & 0x01) {
-		parport_pc_epp_clear_timeout(pb);
-		return PARPORT_MODE_PCEPP;
-	}
-
-	/*
-	 * Theory:
-	 *     Write two values to the EPP address register and
-	 *     read them back. When the transfer times out, the state of
-	 *     the EPP register is undefined in some cases (EPP 1.9?) but
-	 *     in others (EPP 1.7, ECPEPP?) it is possible to read back
-	 *     its value.
-	 */
-	parport_pc_epp_clear_timeout(pb);
-	udelay(30); /* Wait for possible EPP timeout */
-
-	/* Previous test left outputs disabled. */
-	outb (0x55, EPPADDR (pb));
-
-	parport_pc_epp_clear_timeout(pb);
-	udelay(30); /* Wait for possible EPP timeout */
-
-	/* We must enable the outputs to be able to read the address
-           register. */
-	parport_pc_frob_control (pb, 0x20, 0x00);
-
-	if (inb (EPPADDR (pb)) == 0x55) {
-
-		/* wash ... */
-		parport_pc_frob_control (pb, 0x20, 0x20);
-		outb (0xaa, EPPADDR (pb));
-
-		parport_pc_epp_clear_timeout(pb);
-		udelay(30); /* Wait for possible EPP timeout */
-
-		/* ... and repeat */
-		parport_pc_frob_control (pb, 0x20, 0x00);
+	/* If EPP timeout bit clear then EPP available */
+	if (!clear_epp_timeout(pb))
+		return 0;  /* No way to clear timeout */
 
-		if (inb (EPPADDR (pb)) == 0xaa) {
-			parport_pc_epp_clear_timeout (pb);
-			return PARPORT_MODE_PCEPP;
+	/* Check for Intel bug. */
+	if (priv->ecr) {
+		unsigned char i;
+		for (i = 0x00; i < 0x80; i += 0x20) {
+			outb (i, ECONTROL (pb));
+			if (clear_epp_timeout (pb))
+				/* Phony EPP in ECP. */
+				return 0;
 		}
 	}
 
-	return 0;
+	pb->modes |= PARPORT_MODE_EPP;
+
+	/* Set up access functions to use EPP hardware. */
+	parport_pc_ops.epp_read_data = parport_pc_epp_read_data;
+	parport_pc_ops.epp_write_data = parport_pc_epp_write_data;
+	parport_pc_ops.epp_read_addr = parport_pc_epp_read_addr;
+	parport_pc_ops.epp_write_addr = parport_pc_epp_write_addr;
+
+	return 1;
 }
 
 static int __init parport_ECPEPP_supported(struct parport *pb)
 {
-	int mode;
+	struct parport_pc_private *priv = pb->private_data;
+	int result;
 	unsigned char oecr;
 
-	if (!(pb->modes & PARPORT_MODE_PCECR))
+	if (!priv->ecr)
 		return 0;
 
-	oecr = parport_pc_read_econtrol(pb);
+	oecr = inb (ECONTROL (pb));
 	/* Search for SMC style EPP+ECP mode */
-	parport_pc_write_econtrol(pb, 0x80);
+	outb (0x80, ECONTROL (pb));
 	
-	mode = parport_EPP_supported(pb);
+	result = parport_EPP_supported(pb);
 
-	parport_pc_write_econtrol(pb, oecr);
-	
-	return mode?PARPORT_MODE_PCECPEPP:0;
-}
-
-/* Detect PS/2 support.
- *
- * Bit 5 (0x20) sets the PS/2 data direction; setting this high
- * allows us to read data from the data lines.  In theory we would get back
- * 0xff but any peripheral attached to the port may drag some or all of the
- * lines down to zero.  So if we get back anything that isn't the contents
- * of the data register we deem PS/2 support to be present. 
- *
- * Some SPP ports have "half PS/2" ability - you can't turn off the line
- * drivers, but an external peripheral with sufficiently beefy drivers of
- * its own can overpower them and assert its own levels onto the bus, from
- * where they can then be read back as normal.  Ports with this property
- * and the right type of device attached are likely to fail the SPP test,
- * (as they will appear to have stuck bits) and so the fact that they might
- * be misdetected here is rather academic. 
- */
+	outb (oecr, ECONTROL (pb));
 
-static int __init parport_PS2_supported(struct parport *pb)
-{
-	int ok = 0;
-	unsigned char octr = parport_pc_read_control(pb);
-  
-	parport_pc_epp_clear_timeout(pb);
-
-	parport_pc_write_control(pb, octr | 0x20);  /* try to tri-state the buffer */
-	
-	parport_pc_write_data(pb, 0x55);
-	if (parport_pc_read_data(pb) != 0x55) ok++;
-
-	parport_pc_write_data(pb, 0xaa);
-	if (parport_pc_read_data(pb) != 0xaa) ok++;
-	
-	parport_pc_write_control(pb, octr);          /* cancel input mode */
+	if (result) {
+		/* Set up access functions to use ECP+EPP hardware. */
+		parport_pc_ops.epp_read_data = parport_pc_ecpepp_read_data;
+		parport_pc_ops.epp_write_data = parport_pc_ecpepp_write_data;
+		parport_pc_ops.epp_read_addr = parport_pc_ecpepp_read_addr;
+		parport_pc_ops.epp_write_addr = parport_pc_ecpepp_write_addr;
+	}
 
-	return ok?PARPORT_MODE_PCPS2:0;
+	return result;
 }
 
-static int __init parport_ECPPS2_supported(struct parport *pb)
-{
-	int mode;
-	unsigned char oecr;
-
-	if (!(pb->modes & PARPORT_MODE_PCECR))
-		return 0;
+#else /* No IEEE 1284 support */
 
-	oecr = parport_pc_read_econtrol(pb);
-	parport_pc_write_econtrol(pb, 0x20);
-	
-	mode = parport_PS2_supported(pb);
+/* Don't bother probing for modes we know we won't use. */
+static int __init parport_PS2_supported(struct parport *pb) { return 0; }
+static int __init parport_ECP_supported(struct parport *pb) { return 0; }
+static int __init parport_EPP_supported(struct parport *pb) { return 0; }
+static int __init parport_ECPEPP_supported(struct parport *pb) { return 0; }
+static int __init parport_ECPPS2_supported(struct parport *pb) { return 0; }
 
-	parport_pc_write_econtrol(pb, oecr);
-	return mode?PARPORT_MODE_PCECPPS2:0;
-}
+#endif /* No IEEE 1284 support */
 
 /* --- IRQ detection -------------------------------------- */
 
@@ -624,17 +1361,17 @@
 static int __init programmable_irq_support(struct parport *pb)
 {
 	int irq, intrLine;
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr = inb (ECONTROL (pb));
 	static const int lookup[8] = {
 		PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
 	};
 
-	parport_pc_write_econtrol(pb,0xE0); /* Configuration MODE */
-	
-	intrLine = (parport_pc_read_configb(pb) >> 3) & 0x07;
+	outb (ECR_CNF << 5, ECONTROL (pb)); /* Configuration MODE */
+
+	intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
 	irq = lookup[intrLine];
 
-	parport_pc_write_econtrol(pb, oecr);
+	outb (oecr, ECONTROL (pb));
 	return irq;
 }
 
@@ -645,15 +1382,16 @@
 	sti();
 	irqs = probe_irq_on();
 		
-	parport_pc_write_econtrol(pb, 0x00);	/* Reset FIFO */
-	parport_pc_write_econtrol(pb, 0xd0);	/* TEST FIFO + nErrIntrEn */
-
-	/* If Full FIFO sure that WriteIntrThresold is generated */
-	for (i=0; i < 1024 && !(parport_pc_read_econtrol(pb) & 0x02) ; i++) 
-		parport_pc_write_fifo(pb, 0xaa);
+	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
+	outb ((ECR_TST << 5) | 0x04, ECONTROL (pb));
+	outb (ECR_TST << 5, ECONTROL (pb));
+
+	/* If Full FIFO sure that writeIntrThreshold is generated */
+	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02) ; i++) 
+		outb (0xaa, FIFO (pb));
 		
 	pb->irq = probe_irq_off(irqs);
-	parport_pc_write_econtrol(pb, 0x00);
+	outb (ECR_SPP << 5, ECONTROL (pb));
 
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;
@@ -671,22 +1409,21 @@
 	return PARPORT_IRQ_NONE;
 #else
 	int irqs;
-	unsigned char octr = parport_pc_read_control(pb);
 	unsigned char oecr;
 
 	if (pb->modes & PARPORT_MODE_PCECR)
-		oecr = parport_pc_read_econtrol(pb);
+		oecr = inb (ECONTROL (pb));
 
 	sti();
 	irqs = probe_irq_on();
 
 	if (pb->modes & PARPORT_MODE_PCECR)
-		parport_pc_frob_econtrol (pb, 0x10, 0x10);
+		frob_econtrol (pb, 0x10, 0x10);
 	
-	parport_pc_epp_clear_timeout(pb);
+	clear_epp_timeout(pb);
 	parport_pc_frob_control (pb, 0x20, 0x20);
 	parport_pc_frob_control (pb, 0x10, 0x10);
-	parport_pc_epp_clear_timeout(pb);
+	clear_epp_timeout(pb);
 
 	/* Device isn't expecting an EPP read
 	 * and generates an IRQ.
@@ -696,56 +1433,20 @@
 
 	pb->irq = probe_irq_off (irqs);
 	if (pb->modes & PARPORT_MODE_PCECR)
-		parport_pc_write_econtrol(pb, oecr);
-	parport_pc_write_control(pb, octr);
+		outb (oecr, ECONTROL (pb));
+	parport_pc_write_control(pb, 0xc);
 
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;
 
 	return pb->irq;
-#endif /* Advanced detection. */
+#endif /* Advanced detection */
 }
 
 static int __init irq_probe_SPP(struct parport *pb)
 {
-#ifndef ADVANCED_DETECT
 	/* Don't even try to do this. */
 	return PARPORT_IRQ_NONE;
-#else
-	int irqs;
-	unsigned char octr = parport_pc_read_control(pb);
-	unsigned char oecr;
-
-	if (pb->modes & PARPORT_MODE_PCECR)
-		oecr = parport_pc_read_econtrol(pb);
-	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
-	irqs = probe_irq_on();
-
-	if (pb->modes & PARPORT_MODE_PCECR)
-		parport_pc_write_econtrol(pb, 0x10);
-
-	parport_pc_write_data(pb,0x00);
-	parport_pc_write_control(pb,0x00);
-	parport_pc_write_control(pb,0x0c);
-	udelay(5);
-	parport_pc_write_control(pb,0x0d);
-	udelay(5);
-	parport_pc_write_control(pb,0x0c);
-	udelay(25);
-	parport_pc_write_control(pb,0x08);
-	udelay(25);
-	parport_pc_write_control(pb,0x0c);
-	udelay(50);
-
-	pb->irq = probe_irq_off(irqs);
-	if (pb->irq <= 0)
-		pb->irq = PARPORT_IRQ_NONE;	/* No interrupt detected */
-	
-	if (pb->modes & PARPORT_MODE_PCECR)
-		parport_pc_write_econtrol(pb, oecr);
-	parport_pc_write_control(pb, octr);
-	return pb->irq;
-#endif /* Advanced detection. */
 }
 
 /* We will attempt to share interrupt requests since other devices
@@ -757,25 +1458,27 @@
  */
 static int __init parport_irq_probe(struct parport *pb)
 {
-	if (pb->modes & PARPORT_MODE_PCECR) {
+	const struct parport_pc_private *priv = pb->private_data;
+
+	if (priv->ecr) {
 		pb->irq = programmable_irq_support(pb);
 		if (pb->irq != PARPORT_IRQ_NONE)
 			goto out;
 	}
 
-	if (pb->modes & PARPORT_MODE_PCECP)
+	if (pb->modes & PARPORT_MODE_ECP)
 		pb->irq = irq_probe_ECP(pb);
 
-	if (pb->irq == PARPORT_IRQ_NONE && 
-	    (pb->modes & PARPORT_MODE_PCECPEPP))
+	if (pb->irq == PARPORT_IRQ_NONE && priv->ecr &&
+	    (pb->modes & PARPORT_MODE_EPP))
 		pb->irq = irq_probe_EPP(pb);
 
-	parport_pc_epp_clear_timeout(pb);
+	clear_epp_timeout(pb);
 
-	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_PCEPP))
+	if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
 		pb->irq = irq_probe_EPP(pb);
 
-	parport_pc_epp_clear_timeout(pb);
+	clear_epp_timeout(pb);
 
 	if (pb->irq == PARPORT_IRQ_NONE)
 		pb->irq = irq_probe_SPP(pb);
@@ -784,6 +1487,33 @@
 	return pb->irq;
 }
 
+/* --- DMA detection -------------------------------------- */
+
+/* Only if supports ECP mode */
+static int __init programmable_dma_support (struct parport *p)
+{
+	unsigned char oecr = inb (ECONTROL (p));
+	int dma;
+
+	frob_econtrol (p, 0xe0, ECR_CNF << 5);
+	
+	dma = inb (CONFIGB(p)) & 0x03;
+	if (!dma)
+		dma = PARPORT_DMA_NONE;
+
+	outb (oecr, ECONTROL (p));
+	return dma;
+}
+
+static int __init parport_dma_probe (struct parport *p)
+{
+	const struct parport_pc_private *priv = p->private_data;
+	if (priv->ecr)
+		p->dma = programmable_dma_support(p);
+
+	return p->dma;
+}
+
 /* --- Initialisation code -------------------------------- */
 
 static int __init probe_one_port(unsigned long int base,
@@ -801,6 +1531,10 @@
 		return 0;
 	}
 	priv->ctr = 0xc;
+	priv->ctr_writable = 0xff;
+	priv->ecr = 0;
+	priv->fifo_depth = 0;
+	priv->dma_buf = 0;
 	p->base = base;
 	p->base_hi = base_hi;
 	p->irq = irq;
@@ -808,39 +1542,41 @@
 	p->modes = PARPORT_MODE_PCSPP;
 	p->ops = &parport_pc_ops;
 	p->private_data = priv;
-	if (base_hi && !check_region (base_hi, 3)) {
-		p->modes |= parport_ECR_present (p);
-		p->modes |= parport_ECP_supported (p);
-		p->modes |= parport_ECPPS2_supported (p);
+	p->physport = p;
+	if (base_hi && !check_region(base_hi,3)) {
+		parport_ECR_present(p);
+		parport_ECP_supported(p);
+		parport_ECPPS2_supported(p);
 	}
-	if (p->base != 0x3bc) {
+	if (base != 0x3bc) {
 		if (!check_region(base+0x3, 5)) {
-			p->modes |= parport_EPP_supported (p);
-			p->modes |= parport_ECPEPP_supported (p);
+			parport_EPP_supported(p);
+			if (!(p->modes & PARPORT_MODE_EPP))
+				parport_ECPEPP_supported(p);
 		}
 	}
-	if (!parport_SPP_supported(p)) {
+	if (!parport_SPP_supported (p)) {
 		/* No port. */
 		kfree (priv);
 		return 0;
 	}
 
-	p->modes |= parport_PS2_supported(p);
+	parport_PS2_supported (p);
 
-	if (!(p = parport_register_port (base, PARPORT_IRQ_NONE,
-					 PARPORT_DMA_NONE, &parport_pc_ops))) {
+	if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
+									PARPORT_DMA_NONE, &parport_pc_ops))) {
 		kfree (priv);
 		return 0;
 	}
 
 	p->base_hi = base_hi;
 	p->modes = tmp.modes;
-	p->size = (p->modes & PARPORT_MODE_PCEPP) ? 8 : 3;
+	p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
 	p->private_data = priv;
 
 	printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
-	if (p->base_hi && (p->modes & PARPORT_MODE_PCECR))
-		printk (" (0x%lx)", p->base_hi);
+	if (p->base_hi && (p->modes & PARPORT_MODE_ECP))
+		printk(" (0x%lx)", p->base_hi);
 	p->irq = irq;
 	p->dma = dma;
 	if (p->irq == PARPORT_IRQ_AUTO) {
@@ -852,33 +1588,54 @@
 		probedirq = p->irq;
 		p->irq = PARPORT_IRQ_NONE;
 	}
-	if (p->irq != PARPORT_IRQ_NONE)
+	if (p->irq != PARPORT_IRQ_NONE) {
 		printk(", irq %d", p->irq);
+
+		if (p->dma == PARPORT_DMA_AUTO) {
+			p->dma = PARPORT_DMA_NONE;
+			parport_dma_probe(p);
+		}
+	}
 	if (p->dma == PARPORT_DMA_AUTO)		
 		p->dma = PARPORT_DMA_NONE;
-	if (p->dma != PARPORT_DMA_NONE)
+	if (p->dma != PARPORT_DMA_NONE) 
 		printk(", dma %d", p->dma);
+
+#ifdef CONFIG_PARPORT_PC_FIFO
+	if (priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
+		parport_pc_ops.compat_write_data =
+			parport_pc_compat_write_block_pio;
+#ifdef CONFIG_PARPORT_1284
+		parport_pc_ops.ecp_write_data =
+			parport_pc_ecp_write_block_pio;
+#endif /* IEEE 1284 support */
+		if (p->dma != PARPORT_DMA_NONE)
+			p->modes |= PARPORT_MODE_DMA;
+		printk(", using FIFO");
+	}
+#endif /* Allowed to use FIFO/DMA */
+
 	printk(" [");
-#define printmode(x) {if(p->modes&PARPORT_MODE_PC##x){printk("%s%s",f?",":"",#x);f++;}}
+#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
 	{
 		int f = 0;
-		printmode(SPP);
-		printmode(PS2);
+		printmode(PCSPP);
+		printmode(TRISTATE);
+		printmode(COMPAT)
 		printmode(EPP);
 		printmode(ECP);
-		printmode(ECPEPP);
-		printmode(ECPPS2);
+		printmode(DMA);
 	}
 #undef printmode
 	printk("]\n");
-#ifdef	CONFIG_PROC_FS
 	if (probedirq != PARPORT_IRQ_NONE) 
-		printk("%s: detected irq %d; use procfs to enable interrupt-driven operation.\n", p->name, probedirq);
-#endif
+		printk("%s: irq %d detected\n", p->name, probedirq);
 	parport_proc_register(p);
 
-	request_region (p->base, p->size, p->name);
-	if (p->modes & PARPORT_MODE_PCECR)
+	request_region (p->base, 3, p->name);
+	if (p->size > 3)
+		request_region (p->base + 3, p->size - 3, p->name);
+	if (p->modes & PARPORT_MODE_ECP)
 		request_region (p->base_hi, 3, p->name);
 
 	if (p->irq != PARPORT_IRQ_NONE) {
@@ -897,25 +1654,38 @@
 					"resorting to PIO operation\n",
 					p->name, p->dma);
 				p->dma = PARPORT_DMA_NONE;
+			} else {
+				priv->dma_buf = (char *) __get_dma_pages(GFP_KERNEL, 1);
+				if (! priv->dma_buf) {
+					printk (KERN_WARNING "%s: "
+						"cannot get buffer for DMA, "
+						"resorting to PIO operation\n",
+						p->name);
+					free_dma(p->dma);
+					p->dma = PARPORT_DMA_NONE;
+				}
 			}
 		}
 	}
 
-	/* Done probing.  Now put the port into a sensible start-up state. */
-	if (p->modes & PARPORT_MODE_PCECR)
+	/* Done probing.  Now put the port into a sensible start-up state.
+	 * SELECT | INIT also puts IEEE1284-compliant devices into
+	 * compatibility mode. */
+	if (p->modes & PARPORT_MODE_ECP)
 		/*
 		 * Put the ECP detected port in PS2 mode.
 		 */
-		parport_pc_write_econtrol(p, 0x24);
+		outb (0x24, ECONTROL (p));
+
 	parport_pc_write_data(p, 0);
-	parport_pc_write_control(p, 0x8);
+	parport_pc_data_forward (p);
+	parport_pc_write_control(p, PARPORT_CONTROL_SELECT);
 	udelay (50);
-	parport_pc_write_control(p, 0xc);
+	parport_pc_write_control(p,
+				 PARPORT_CONTROL_SELECT
+				 | PARPORT_CONTROL_INIT);
 	udelay (50);
 
-	if (parport_probe_hook)
-		(*parport_probe_hook)(p);
-
 	/* Now that we've told the sharing engine about the port, and
 	   found out its characteristics, let the high-level drivers
 	   know about it. */
@@ -953,11 +1723,8 @@
 #define PCI_DEVICE_ID_LAVA_PARALLEL     0x8000
 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_A   0x8001 /* The Lava Dual Parallel is */
 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_B   0x8002 /* two PCI devices on a card */
-#endif /* IDs not defined */
+#endif
 
-	int count = 0;
-#ifdef CONFIG_PCI
-	int i;
 	struct {
 		unsigned int vendor;
 		unsigned int device;
@@ -1014,6 +1781,9 @@
 		{ 0, }
 	};
 
+	int count = 0;
+	int i;
+
 	if (!pci_present ())
 		return 0;
 
@@ -1036,7 +1806,6 @@
 			}
 		}
 	}
-#endif /* CONFIG_PCI */
 
 	return count;
 }
@@ -1070,17 +1839,9 @@
 static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
 static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
 static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-
-MODULE_PARM_DESC(io, "base address");
 MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
-
-MODULE_PARM_DESC(io_hi, "base address for ECR");
 MODULE_PARM(io_hi, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
-
-MODULE_PARM_DESC(irq, "irq line to use (or 'auto' or 'none')");
 MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-
-MODULE_PARM_DESC(dma, "dma channel to use (or 'auto' or 'none')");
 MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
 
 int init_module(void)
@@ -1120,15 +1881,20 @@
 	struct parport *p = parport_enumerate(), *tmp;
 	while (p) {
 		tmp = p->next;
-		if (p->modes & PARPORT_MODE_PCSPP) {
+		if (p->modes & PARPORT_MODE_PCSPP) { 
+			struct parport_pc_private *priv = p->private_data;
 			if (p->dma != PARPORT_DMA_NONE)
-				free_dma (p->dma);
+				free_dma(p->dma);
 			if (p->irq != PARPORT_IRQ_NONE)
-				free_irq (p->irq, p);
-			release_region (p->base, p->size);
-			if (p->modes & PARPORT_MODE_PCECP)
-				release_region (p->base_hi, 3);
+				free_irq(p->irq, p);
+			release_region(p->base, 3);
+			if (p->size > 3);
+				release_region(p->base + 3, p->size - 3);
+			if (p->modes & PARPORT_MODE_ECP)
+				release_region(p->base_hi, 3);
 			parport_proc_unregister(p);
+			if (priv->dma_buf)
+				free_page((unsigned long) priv->dma_buf);
 			kfree (p->private_data);
 			parport_unregister_port(p);
 		}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)