Resent-Date: Sat, 3 Oct 1998 11:49:01 +0200 (MET DST)
Subject: Parport for MFC3
To: linux-m68k@phil.uni-sb.de (Linux Liste)
Date: Sat, 3 Oct 1998 10:48:42 +0100 (MET)
From: Joerg Dorchain <dorchain@wirbel.com>
Cc: Jes.Sorensen@cern.ch (Jes Sorensen)
Resent-From: linux-m68k@phil.uni-sb.de

 Hi all,

 this is a patch against 2.1.20 for including parport support for the
 amiga built-in port and the parallel port of a Multiface 3 card.
 Please test it and tell the results. As high-level-driver only the
 printer driver (lp) is currently tested (due to the lack of zip-drives,
 scanners, ....). At least the MFC3 port should be able to work with
 those things.

 Have fun,

 Joerg

 
--- ./arch/m68k/config.in.old	Tue Sep  8 18:27:47 1998
+++ ./arch/m68k/config.in	Sun Sep 20 07:57:51 1998
@@ -103,6 +103,18 @@
   fi
 fi
 bool '/proc/hardware support' CONFIG_PROC_HARDWARE
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Parallel port support (disables old lp driver!)' CONFIG_PARPORT
+  if [ "$CONFIG_PARPORT" != "n" ]; then
+    if [ "$CONFIG_AMIGA" != "n" ]; then
+      dep_tristate '   Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT
+      if [ "$CONFIG_ZORRO" != "n" ]; then
+        dep_tristate '    Multiface III parallel port' CONFIG_PARPORT_MFC3 $CONFIG_PARPORT
+      fi
+    fi
+  fi
+fi
 endmenu
 
 source drivers/block/Config.in
@@ -259,9 +271,16 @@
   define_bool CONFIG_NVRAM y
 fi
 
-tristate 'Parallel printer support' CONFIG_M68K_PRINTER
-if [ "$CONFIG_ZORRO" = "y" ]; then
-  dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_PRINTER
+if [ "$CONFIG_PARPORT" = "n" ]; then
+  tristate 'Parallel printer support' CONFIG_M68K_PRINTER
+  if [ "$CONFIG_ZORRO" = "y" ]; then
+    dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_PRINTER
+  fi
+else
+  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
+  if [ "$CONFIG_PRINTER" != "n" ]; then
+    bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
+  fi
 fi
 if [ "$CONFIG_AMIGA" = "y" ]; then
   tristate 'Amiga mouse support' CONFIG_AMIGAMOUSE
--- ./drivers/char/lp.c.old	Sun Aug 30 13:33:56 1998
+++ ./drivers/char/lp.c	Sun Sep 27 14:26:39 1998
@@ -94,13 +94,17 @@
 #include <linux/delay.h>
 
 #include <linux/parport.h>
-#undef LP_STATS
+#define LP_STATS
 #undef LP_NEED_CAREFUL
 #include <linux/lp.h>
 
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <asm/spinlock.h>
+
+spinlock_t lp_lock;
+
 
 /* if you have more than 3 printers, remember to increase LP_NO */
 #define LP_NO 3
@@ -126,7 +130,7 @@
 #define LP_READY(minor, status) ((status) & LP_PBUSY)
 #endif
 
-#undef LP_DEBUG
+#define LP_DEBUG
 #undef LP_READ_DEBUG
 
 /* --- parport support ----------------------------------------- */
@@ -211,20 +215,12 @@
 #endif
 	/* must wait before taking strobe high, and after taking strobe
 	   low, according spec.  Some printers need it, others don't. */
-#ifndef __sparc__
-	while (wait != LP_WAIT(minor)) /* FIXME: should be a udelay() */
-		wait++;
-#else
-	udelay(1);
-#endif
+	udelay(LP_WAIT(minor));
+
 	/* control port takes strobe high */
 	w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
-#ifndef __sparc__
-	while (wait)			/* FIXME: should be a udelay() */
-		wait--;
-#else
-	udelay(1);
-#endif
+	udelay(LP_WAIT(minor));
+
 	/* take strobe low */
 	if (LP_POLLED(minor))
 		/* take strobe low */
@@ -240,7 +236,7 @@
 	/* update waittime statistics */
 	if (count > stats->maxwait) {
 #ifdef LP_DEBUG
-		printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count);
+		printk(KERN_DEBUG "lp%d success after %ld counts.\n", minor, count);
 #endif
 		stats->maxwait = count;
 	}
@@ -384,7 +380,12 @@
 					current->timeout = jiffies + LP_TIME(minor);
 					lp_schedule (minor);
 				} else {
-					cli();
+					/* Replace cli()/sti() by
+					  cli()/restore_flags.
+					  It's a must for other architectures
+					  and doesn't break i386 */
+					unsigned long flags;
+					spin_lock_irqsave(&lp_lock, flags);
 					if (LP_PREEMPTED(minor))
 					{
 						/*
@@ -394,7 +395,7 @@
 						 * envinroment to avoid parport sharing
 						 * starvation.
 						 */
-						sti();
+						spin_unlock_irqrestore(&lp_lock, flags);
 						goto lp_polling;
 					}
 					if (!lp_table[minor].irq_detected)
@@ -402,7 +403,7 @@
 						current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
 						interruptible_sleep_on(&lp->wait_q);
 					}
-					sti();
+					spin_unlock_irqrestore(&lp_lock, flags);
 				}
 			}
 		}
@@ -624,7 +625,7 @@
 	int retval = 0;
 
 #ifdef LP_DEBUG
-	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%0x, arg: 0x%08lx\n", minor, cmd, arg);
 #endif
 	if (minor >= LP_NO)
 		return -ENODEV;
@@ -788,6 +789,8 @@
 	unsigned int count = 0;
 	unsigned int i;
 	struct parport *port;
+
+	spin_lock_init(&lp_lock);
 
 	switch (parport_nr[0])
 	{
--- ./drivers/misc/parport_ax.c.old	Mon Aug 10 17:37:16 1998
+++ ./drivers/misc/parport_ax.c	Sun Sep 27 14:05:53 1998
@@ -50,12 +50,6 @@
 #define CONFIGB		0x401
 #define ECONTROL	0x402
 
-static void
-parport_ax_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-	/* NULL function - Does nothing */
-}
-
 void
 parport_ax_write_epp(struct parport *p, unsigned char d)
 {
@@ -206,7 +200,7 @@
 {
 	if (p->irq != PARPORT_IRQ_NONE) {
 		parport_ax_disable_irq(p);
-		free_irq(p->irq, NULL);
+		free_irq(p->irq, p);
 	}
 	release_region(p->base, p->size);
 	if (p->modes & PARPORT_MODE_PCECR)
@@ -220,8 +214,8 @@
 {
 	/* FIXME check that resources are free */
 	if (p->irq != PARPORT_IRQ_NONE) {
-		request_irq(p->irq, parport_ax_null_intr_func,
-			    0, p->name, NULL);
+		request_irq(p->irq, parport_intr_func,
+			    0, p->name, p);
 		parport_ax_enable_irq(p);
 	}
 	request_region(p->base, p->size, p->name);
@@ -282,7 +276,7 @@
 }
 
 int
-parport_ax_examine_irq(struct parport *p)
+parport_ax_change_irq(struct parport *p,int newirq)
 {
 	return 0; /* FIXME */
 }
@@ -355,7 +349,7 @@
 
 	parport_ax_enable_irq,
 	parport_ax_disable_irq,
-	parport_ax_examine_irq,
+	parport_ax_change_irq,
 
 	parport_ax_inc_use_count,
 	parport_ax_dec_use_count,
--- ./drivers/misc/parport_init.c.old	Thu Dec 11 06:23:33 1902
+++ ./drivers/misc/parport_init.c	Sun Sep 20 07:54:35 1998
@@ -122,6 +122,12 @@
 #ifdef CONFIG_PARPORT_AX
 	parport_ax_init();
 #endif
+#ifdef CONFIG_PARPORT_AMIGA
+	parport_amiga_init();
+#endif
+#ifdef CONFIG_PARPORT_MFC3
+	parport_mfc3_init();
+#endif
 	return 0;
 }
 #endif
@@ -143,6 +149,7 @@
 EXPORT_SYMBOL(parport_proc_unregister);
 EXPORT_SYMBOL(parport_probe_hook);
 EXPORT_SYMBOL(parport_parse_irqs);
+EXPORT_SYMBOL(parport_intr_func);
 
 void inc_parport_count(void)
 {
--- ./drivers/misc/parport_pc.c.old	Mon Aug 10 17:37:16 1998
+++ ./drivers/misc/parport_pc.c	Sun Sep 27 14:07:56 1998
@@ -53,11 +53,6 @@
    than PARPORT_MAX (in <linux/parport.h>).  */
 #define PARPORT_PC_MAX_PORTS  8
 
-static void parport_pc_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-	/* Null function - does nothing */
-}
-
 void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
 	outb(d, p->base+EPPDATA);
@@ -173,7 +168,7 @@
 void parport_pc_release_resources(struct parport *p)
 {
 	if (p->irq != PARPORT_IRQ_NONE)
-		free_irq(p->irq, NULL);
+		free_irq(p->irq, p);
 	release_region(p->base, p->size);
 	if (p->modes & PARPORT_MODE_PCECR)
 		release_region(p->base+0x400, 3);
@@ -183,7 +178,7 @@
 {
 	int err;
 	if (p->irq != PARPORT_IRQ_NONE)
-		if ((err = request_irq(p->irq, parport_pc_null_intr_func, 0, p->name, NULL)) != 0) return err;
+		if ((err = request_irq(p->irq, parport_intr_func, 0, p->name, p)) != 0) return err;
 	request_region(p->base, p->size, p->name);
 	if (p->modes & PARPORT_MODE_PCECR)
 		request_region(p->base+0x400, 3, p->name);
@@ -242,7 +237,7 @@
 	return -ENOSYS; /* FIXME */
 }
 
-int parport_pc_examine_irq(struct parport *p)
+int parport_pc_change_irq(struct parport *p, int newirq)
 {
 	return 0; /* FIXME */
 }
@@ -313,7 +308,7 @@
 
 	parport_pc_enable_irq,
 	parport_pc_disable_irq,
-	parport_pc_examine_irq,
+	parport_pc_change_irq,
 
 	parport_pc_inc_use_count,
 	parport_pc_dec_use_count,
--- ./drivers/misc/parport_procfs.c.old	Mon Aug 10 17:37:16 1998
+++ ./drivers/misc/parport_procfs.c	Sun Sep 27 13:32:00 1998
@@ -65,36 +65,29 @@
 		goto out;
 	}
 
-	retval = count;
-
 	if (oldirq == newirq)
-		goto out;
+		return count;
 
-	spin_lock_irqsave(&pp->lock, flags);
-	if (pp->flags & PARPORT_FLAG_COMA)
-		goto out_ok;
+	read_lock_irqsave(&pp->cad_lock, flags);
 
-	retval = -EBUSY;
 	if (pp->cad)
-		goto out_unlock;
-
-	if (newirq != PARPORT_IRQ_NONE) { 
-		retval = request_irq(newirq, parport_null_intr_func,
-				     SA_INTERRUPT, pp->name, NULL);
-		if (retval)
-			goto out_unlock;
-		else retval = count;
+	{
+		retval = -EBUSY;
+		goto unlock_and_return;
 	}
 
-	if (oldirq != PARPORT_IRQ_NONE)
-		free_irq(oldirq, NULL);
-
-out_ok:
-	pp->irq = newirq;
-
-out_unlock:
-	spin_unlock_irqrestore (&pp->lock, flags);
 
+	if (pp->ops->change_irq)
+	{
+		retval = pp->ops->change_irq(pp, newirq);
+		if (!retval)
+			retval = count;
+	}
+	else
+		retval = -ENOSYS;
+	
+unlock_and_return:
+	read_unlock_irqrestore(&pp->cad_lock, flags);
 out:
 	return retval;
 }
--- ./drivers/misc/parport_share.c.old	Mon Aug 10 17:37:16 1998
+++ ./drivers/misc/parport_share.c	Tue Sep  8 18:51:52 1998
@@ -55,10 +55,15 @@
 	return portlist;
 }
 
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+void parport_intr_func(int irq, void *dev_id, struct pt_regs *regs)
 {
-	/* Null function - does nothing.  IRQs are pointed here whenever
-	   there is no real handler for them.  */
+	/* Generic function. This has always to be done. If some hardware
+	   needs black magic before or after the high level call,
+	   implement an own function. */
+	struct parport *p = dev_id;
+
+	if ( p && p->cad && p->cad->irq_func )
+		(*p->cad->irq_func)(irq, p->cad->private, regs);
 }
 
 struct parport *parport_register_port(unsigned long base, int irq, int dma,
@@ -355,19 +360,7 @@
 	port->cad = dev;
 	spin_unlock_irqrestore(&port->lock, flags);
 
-	/* Swap the IRQ handlers. */
-	if (port->irq != PARPORT_IRQ_NONE) {
-		if (oldcad && oldcad->irq_func) {
-			free_irq(port->irq, oldcad->private);
-			request_irq(port->irq, parport_null_intr_func,
-				    SA_INTERRUPT, port->name, NULL);
-		}
-		if (dev->irq_func) {
-			free_irq(port->irq, NULL);
-			request_irq(port->irq, dev->irq_func,
-				    SA_INTERRUPT, dev->name, dev->private);
-		}
-	}
+	/* irqs are done by low level driver */
 
 	/* Restore control registers */
 	port->ops->restore_state(port, dev->state);
@@ -457,13 +450,6 @@
 
 	/* Save control registers */
 	port->ops->save_state(port, dev->state);
-
-	/* Point IRQs somewhere harmless. */
-	if (port->irq != PARPORT_IRQ_NONE && dev->irq_func) {
-		free_irq(port->irq, dev->private);
-		request_irq(port->irq, parport_null_intr_func,
-			    SA_INTERRUPT, port->name, NULL);
- 	}
 
 	/* If anybody is waiting, find out who's been there longest and
 	   then wake them up. (Note: no locking required) */
--- ./drivers/misc/Makefile.old	Mon Feb 23 21:26:49 1998
+++ ./drivers/misc/Makefile	Sun Sep 20 07:53:53 1998
@@ -37,6 +37,20 @@
       M_OBJS += parport_ax.o
     endif
   endif
+  ifeq ($(CONFIG_PARPORT_AMIGA),y)
+    LX_OBJS += parport_amiga.o
+  else
+    ifeq ($(CONFIG_PARPORT_AMIGA),m)
+      M_OBJS += parport_amiga.o
+    endif
+  endif
+  ifeq ($(CONFIG_PARPORT_MFC3),y)
+    LX_OBJS += parport_mfc3.o
+  else
+    ifeq ($(CONFIG_PARPORT_MFC3),m)
+      M_OBJS += parport_mfc3.o
+    endif
+  endif
   LX_OBJS += parport_init.o
 else
   ifeq ($(CONFIG_PARPORT),m)
@@ -52,6 +66,12 @@
   endif
   ifeq ($(CONFIG_PARPORT_AX),m)
     M_OBJS += parport_ax.o
+  endif
+  ifeq ($(CONFIG_PARPORT_AMIGA),m)
+    M_OBJS += parport_amiga.o
+  endif
+  ifeq ($(CONFIG_PARPORT_MFC3),m)
+    M_OBJS += parport_mfc3.o
   endif
 endif
 
--- ./drivers/misc/parport_amiga.c.old	Tue Sep  8 18:51:52 1998
+++ ./drivers/misc/parport_amiga.c	Sun Sep 27 14:36:36 1998
@@ -0,0 +1,301 @@
+/* Low-level parallel port routines for the Amiga buildin port
+ *
+ * Author: Joerg Dorchain <dorchain@wirbel.com>
+ *
+ * This is a complete rewrite of the code, but based heaviy upon the old
+ * lp_intern. code.
+ *
+ * The built-in Amiga parallel port provides one port at a fixed address
+ * with 8 bisdirecttional data lines (D0 - D7) and 3 bidirectional status
+ * lines (BUSY, POUT, SEL), 1 output control line /STROBE (raised automatically in
+ * hardware when the data register is accessed), and 1 input control line
+ * /ACK, able to cause an interrupt, but both not directly settable by
+ * software.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+static inline int DPRINTK() {return 0;}
+#endif
+
+static struct parport *this_port = NULL;
+
+static void amiga_write_data(struct parport *p, unsigned char data)
+{
+DPRINTK("write_data %c\n",data);
+	/* Triggers also /STROBE. This behavior cannot be changed */
+	ciaa.prb = data;
+}
+
+static unsigned char amiga_read_data(struct parport *p)
+{
+	/* Triggers also /STROBE. This behavior cannot be changed */
+	return ciaa.prb;
+}
+
+#if 0
+static unsigned char control_pc_to_amiga(unsigned char control)
+{
+	unsigned char ret = 0;
+
+	if (control & PARPORT_CONTROL_DIRECTION) /* XXX: What is this? */
+		;
+	if (control & PARPORT_CONTROL_INTEN) /* XXX: What is INTEN? */
+		;
+	if (control & PARPORT_CONTROL_SELECT) /* XXX: What is SELECP? */
+		;
+	if (control & PARPORT_CONTROL_INIT) /* INITP */
+		/* reset connected to cpu reset pin */;
+	if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */
+		/* Not connected */;
+	if (control & PARPORT_CONTROL_STROBE) /* Strobe */
+		/* Handled only directly by hardware */;
+	return ret;
+}
+#endif
+
+static unsigned char control_amiga_to_pc(unsigned char control)
+{
+	return PARPORT_CONTROL_INTEN | PARPORT_CONTROL_SELECT |
+	      PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE;
+	/* fake value: interrupt enable, select in, no reset,
+	no autolf, no strobe - seems to be closest the wiring diagram */
+}
+
+static void amiga_write_control(struct parport *p, unsigned char control)
+{
+DPRINTK("write_control %02x\n",control);
+	/* No implementation possible */
+}
+	
+static unsigned char amiga_read_control( struct parport *p)
+{
+DPRINTK("read_control \n");
+	return control_amiga_to_pc(0);
+}
+
+static unsigned char amiga_frob_control( struct parport *p, unsigned char mask, unsigned char val)
+{
+	unsigned char old;
+
+DPRINTK("frob_control mask %02x, value %02x\n",mask,val);
+	old = amiga_read_control(p);
+	amiga_write_control(p, (old & ~mask) ^ val);
+	return old;
+}
+
+
+static unsigned char status_pc_to_amiga(unsigned char status)
+{
+	unsigned char ret = 1;
+
+	if (status & PARPORT_STATUS_BUSY) /* Busy */
+		ret &= ~1;
+	if (status & PARPORT_STATUS_ACK) /* Ack */
+		/* handled in hardware */;
+	if (status & PARPORT_STATUS_PAPEROUT) /* PaperOut */
+		ret |= 2;
+	if (status & PARPORT_STATUS_SELECT) /* select */
+		ret |= 4;
+	if (status & PARPORT_STATUS_ERROR) /* error */
+		/* not connected */;
+	return ret;
+}
+
+static unsigned char status_amiga_to_pc(unsigned char status)
+{
+	unsigned char ret = PARPORT_STATUS_BUSY | PARPORT_STATUS_ACK | PARPORT_STATUS_ERROR;
+
+	if (status & 1) /* Busy */
+		ret &= ~PARPORT_STATUS_BUSY;
+	if (status & 2) /* PaperOut */
+		ret |= PARPORT_STATUS_PAPEROUT;
+	if (status & 4) /* Selected */
+		ret |= PARPORT_STATUS_SELECT;
+	/* the rest is not connected or handled autonomously in hardware */
+
+	return ret;
+}
+
+static void amiga_write_status( struct parport *p, unsigned char status)
+{
+DPRINTK("write_status %02x\n",status);
+	ciab.pra |= (ciab.pra & 0xf8) | status_pc_to_amiga(status);
+}
+
+static unsigned char amiga_read_status(struct parport *p)
+{
+	unsigned char status;
+
+	status = status_amiga_to_pc(ciab.pra & 7);
+DPRINTK("read_status %02x\n", status);
+	return status;
+}
+
+static void amiga_change_mode( struct parport *p, int m)
+{
+	/* XXX: This port only has one mode, and I am
+	not sure about the corresponding PC-style mode*/
+}
+
+/* as this ports irq handling is already done, we use a generic funktion */
+
+static void amiga_release_resources(struct parport *p)
+{
+DPRINTK("realease_resources\n");
+	if (p->irq != PARPORT_IRQ_NONE)
+		free_irq(IRQ_AMIGA_CIAA_FLG, p);
+}
+
+static int amiga_claim_resources(struct parport *p)
+{
+DPRINTK("claim_resources\n");
+	return request_irq(IRQ_AMIGA_CIAA_FLG, parport_intr_func, 0, p->name, p);
+}
+
+static void amiga_init_state(struct parport_state *s)
+{
+	s->u.amiga.data = 0;
+	s->u.amiga.datadir = 255;
+	s->u.amiga.status = 0;
+	s->u.amiga.statusdir = 0;
+}
+
+static void amiga_save_state(struct parport *p, struct parport_state *s)
+{
+	s->u.amiga.data = ciaa.prb;
+	s->u.amiga.datadir = ciaa.ddrb;
+	s->u.amiga.status = ciab.pra & 7;
+	s->u.amiga.statusdir = ciab.ddra & 7;
+}
+
+static void amiga_restore_state(struct parport *p, struct parport_state *s)
+{
+	ciaa.prb = s->u.amiga.data;
+	ciaa.ddrb = s->u.amiga.datadir;
+	ciab.pra |= (ciab.pra & 0xf8) | s->u.amiga.status;
+	ciab.ddra |= (ciab.ddra & 0xf8) | s->u.amiga.statusdir;
+}
+
+static void amiga_enable_irq(struct parport *p)
+{
+	enable_irq(IRQ_AMIGA_CIAA_FLG);
+}
+
+static void amiga_disable_irq(struct parport *p)
+{
+	disable_irq(IRQ_AMIGA_CIAA_FLG);
+}
+
+static void amiga_inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void amiga_dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static struct parport_operations pp_amiga_ops = {
+	amiga_write_data,
+	amiga_read_data,
+
+	amiga_write_control,
+	amiga_read_control,
+	amiga_frob_control,
+
+	NULL, /* write_econtrol */
+	NULL, /* read_econtrol */
+	NULL, /* frob_econtrol */
+
+	amiga_write_status,
+	amiga_read_status,
+
+	NULL, /* write fifo */
+	NULL, /* read fifo */
+
+	amiga_change_mode,
+
+
+	amiga_release_resources,
+	amiga_claim_resources,
+
+
+	NULL, /* epp_write_data */
+	NULL, /* epp_read_data */
+	NULL, /* epp_write_addr */
+	NULL, /* epp_read_addr */
+	NULL, /* epp_check_timeout */
+
+	NULL, /* epp_write_block */
+	NULL, /* epp_read_block */
+
+	NULL, /* ecp_write_block */
+	NULL, /* ecp_read_block */
+
+	amiga_init_state,
+	amiga_save_state,
+	amiga_restore_state,
+
+	amiga_enable_irq,
+	amiga_disable_irq,
+	NULL, /* change_irq */
+
+	amiga_inc_use_count,
+	amiga_dec_use_count
+};
+
+/* ----------- Initialisation code --------------------------------- */
+
+__initfunc(int parport_amiga_init(void))
+{
+	struct parport *p;
+
+	if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) {
+		ciaa.ddrb = 0xff;
+		ciab.ddra &= 0xf8;
+		if (!(p = parport_register_port((unsigned long)&ciaa.prb,
+					IRQ_AMIGA_CIAA_FLG, PARPORT_DMA_NONE,
+					&pp_amiga_ops)))
+			return 0;
+		this_port = p;
+		printk(KERN_INFO "%s: Amiga built-in port using irq\n", p->name);
+		/* XXX: set operating mode */
+		parport_proc_register(p);
+		p->flags |= PARPORT_FLAG_COMA;
+
+		if (parport_probe_hook)
+			(*parport_probe_hook)(p);
+		return 1;
+
+	}
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return ! parport_amiga_init();
+}
+
+void cleanup_module(void)
+{
+	if (!(this_port->flags & PARPORT_FLAG_COMA))
+		parport_quiesce(this_port);
+	parport_proc_unregister(this_port);
+	parport_unregister_port(this_port);
+}
+#endif
+
+
--- ./drivers/misc/parport_mfc3.c.old	Sun Sep 20 04:47:33 1998
+++ ./drivers/misc/parport_mfc3.c	Sun Sep 27 14:03:47 1998
@@ -0,0 +1,404 @@
+/* Low-level parallel port routines for the Multiface 3 card
+ *
+ * Author: Joerg Dorchain <dorchain@wirbel.com>
+ *
+ * (C) The elitist m68k Users(TM)
+ *
+ * based on the existing parport_amiga and lp_mfc
+ *
+ *
+ * From the MFC3 documentation:
+ * 
+ * Miscellaneous PIA Details
+ * -------------------------
+ * 
+ * 	The two open-drain interrupt outputs /IRQA and /IRQB are routed to
+ * /INT2 of the Z2 bus.
+ * 
+ * 	The CPU data bus of the PIA (D0-D7) is connected to D8-D15 on the Z2
+ * bus. This means that any PIA registers are accessed at even addresses.
+ * 
+ * Centronics Pin Connections for the PIA
+ * --------------------------------------
+ * 
+ * 	The following table shows the connections between the PIA and the
+ * Centronics interface connector. These connections implement a single, but
+ * very complete, Centronics type interface. The Pin column gives the pin
+ * numbers of the PIA. The Centronics pin numbers can be found in the section
+ * "Parallel Connectors".
+ * 
+ * 
+ *    Pin | PIA | Dir | Centronics Names
+ * -------+-----+-----+---------------------------------------------------------
+ *     19 | CB2 | --> | /STROBE (aka /DRDY)
+ *  10-17 | PBx | <-> | DATA0 - DATA7
+ *     18 | CB1 | <-- | /ACK
+ *     40 | CA1 | <-- | BUSY
+ *      3 | PA1 | <-- | PAPER-OUT (aka POUT)
+ *      4 | PA2 | <-- | SELECTED (aka SEL)
+ *      9 | PA7 | --> | /INIT (aka /RESET or /INPUT-PRIME)
+ *      6 | PA4 | <-- | /ERROR (aka /FAULT)
+ *      7 | PA5 | --> | DIR (aka /SELECT-IN)
+ *      8 | PA6 | --> | /AUTO-FEED-XT
+ *     39 | CA2 | --> | open
+ *      5 | PA3 | <-- | /ACK (same as CB1!)
+ *      2 | PA0 | <-- | BUSY (same as CA1!)
+ * -------+-----+-----+---------------------------------------------------------
+ * 
+ * Should be enough to understand some of the driver.
+ */
+
+#include "multiface.h"
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/mc6821.h>
+#include <linux/zorro.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+
+/* Maximum Number of Cards supported */
+#define MAX_MFC 5
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+static inline int DPRINTK() {return 0;}
+#endif
+
+static struct parport *this_port[MAX_MFC] = {NULL, };
+static volatile int dummy; /* for trigger readds */
+
+#define pia(dev) ((struct pia *)(dev->base))
+static struct parport_operations pp_mfc3_ops;
+
+static void mfc3_write_data(struct parport *p, unsigned char data)
+{
+DPRINTK("write_data %c\n",data);
+
+	dummy = pia(p)->pprb; /* clears irq bit */
+	/* Triggers also /STROBE.*/
+	pia(p)->pprb = data;
+}
+
+static unsigned char mfc3_read_data(struct parport *p)
+{
+	/* clears interupt bit. Triggers also /STROBE. */
+	return pia(p)->pprb;
+}
+
+static unsigned char control_pc_to_mfc3(unsigned char control)
+{
+	unsigned char ret = 32|64;
+
+	if (control & PARPORT_CONTROL_DIRECTION) /* XXX: What is this? */
+		;
+	if (control & PARPORT_CONTROL_INTEN) /* XXX: What is INTEN? */
+		;
+	if (control & PARPORT_CONTROL_SELECT) /* XXX: What is SELECP? */
+		ret &= ~32; /* /SELECT_IN */
+	if (control & PARPORT_CONTROL_INIT) /* INITP */
+		ret |= 128;
+	if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */
+		ret &= ~64;
+	if (control & PARPORT_CONTROL_STROBE) /* Strobe */
+		/* Handled directly by hardware */;
+	return ret;
+}
+
+static unsigned char control_mfc3_to_pc(unsigned char control)
+{
+	unsigned char ret = PARPORT_CONTROL_INTEN | PARPORT_CONTROL_STROBE 
+			  | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT;
+
+	if (control & 128) /* /INITP */
+		ret |= PARPORT_CONTROL_INIT;
+	if (control & 64) /* /AUTOLF */
+		ret &= ~PARPORT_CONTROL_AUTOFD;
+	if (control & 32) /* /SELECT_IN */
+		ret &= ~PARPORT_CONTROL_SELECT;
+	return ret;
+}
+
+static void mfc3_write_control(struct parport *p, unsigned char control)
+{
+DPRINTK("write_control %02x\n",control);
+	pia(p)->ppra = (pia(p)->ppra & 0x1f) | control_pc_to_mfc3(control);
+}
+	
+static unsigned char mfc3_read_control( struct parport *p)
+{
+DPRINTK("read_control \n");
+	return control_mfc3_to_pc(pia(p)->ppra & 0xe0);
+}
+
+static unsigned char mfc3_frob_control( struct parport *p, unsigned char mask, unsigned char val)
+{
+	unsigned char old;
+
+DPRINTK("frob_control mask %02x, value %02x\n",mask,val);
+	old = mfc3_read_control(p);
+	mfc3_write_control(p, (old & ~mask) ^ val);
+	return old;
+}
+
+
+static unsigned char status_pc_to_mfc3(unsigned char status)
+{
+	unsigned char ret = 1;
+
+	if (status & PARPORT_STATUS_BUSY) /* Busy */
+		ret &= ~1;
+	if (status & PARPORT_STATUS_ACK) /* Ack */
+		ret |= 8;
+	if (status & PARPORT_STATUS_PAPEROUT) /* PaperOut */
+		ret |= 2;
+	if (status & PARPORT_STATUS_SELECT) /* select */
+		ret |= 4;
+	if (status & PARPORT_STATUS_ERROR) /* error */
+		ret |= 16;
+	return ret;
+}
+
+static unsigned char status_mfc3_to_pc(unsigned char status)
+{
+	unsigned char ret = PARPORT_STATUS_BUSY;
+
+	if (status & 1) /* Busy */
+		ret &= ~PARPORT_STATUS_BUSY;
+	if (status & 2) /* PaperOut */
+		ret |= PARPORT_STATUS_PAPEROUT;
+	if (status & 4) /* Selected */
+		ret |= PARPORT_STATUS_SELECT;
+	if (status & 8) /* Ack */
+		ret |= PARPORT_STATUS_ACK;
+	if (status & 16) /* /ERROR */
+		ret |= PARPORT_STATUS_ERROR;
+
+	return ret;
+}
+
+static void mfc3_write_status( struct parport *p, unsigned char status)
+{
+DPRINTK("write_status %02x\n",status);
+	pia(p)->ppra = (pia(p)->ppra & 0xe0) | status_pc_to_mfc3(status);
+}
+
+static unsigned char mfc3_read_status(struct parport *p)
+{
+	unsigned char status;
+
+	status = status_mfc3_to_pc(pia(p)->ppra & 0x1f);
+DPRINTK("read_status %02x\n", status);
+	return status;
+}
+
+static void mfc3_change_mode( struct parport *p, int m)
+{
+	/* XXX: This port only has one mode, and I am
+	not sure about the corresponding PC-style mode*/
+}
+
+static int use_cnt = 0;
+
+static void mfc3_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int i;
+
+	for( i = 0; i < MAX_MFC; i++)
+		if (this_port[i] != NULL)
+			if (pia(this_port[i])->crb & 128) { /* Board caused interrupt */
+				dummy = pia(this_port[i])->pprb; /* clear irq bit */
+				parport_intr_func(irq, this_port[i], regs);
+			}
+}
+
+static void mfc3_release_resources(struct parport *p)
+{
+DPRINTK("realease_resources\n");
+	if (p->irq != PARPORT_IRQ_NONE) 
+		if (--use_cnt == 0) 
+			free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
+}
+
+static int mfc3_claim_resources(struct parport *p)
+{
+DPRINTK("claim_resources\n");
+	if (p->irq != PARPORT_IRQ_NONE)
+		if (use_cnt++ == 0)
+			if (request_irq(IRQ_AMIGA_PORTS, mfc3_intr_func, 0, p->name, &pp_mfc3_ops))
+				 return use_cnt--;
+	return 0;
+}
+
+static void mfc3_init_state(struct parport_state *s)
+{
+	s->u.amiga.data = 0;
+	s->u.amiga.datadir = 255;
+	s->u.amiga.status = 0;
+	s->u.amiga.statusdir = 0xe0;
+}
+
+static void mfc3_save_state(struct parport *p, struct parport_state *s)
+{
+	s->u.amiga.data = pia(p)->pprb;
+	pia(p)->crb &= ~PIA_DDR;
+	s->u.amiga.datadir = pia(p)->pddrb;
+	pia(p)->crb |= PIA_DDR;
+	s->u.amiga.status = pia(p)->ppra;
+	pia(p)->cra &= ~PIA_DDR;
+	s->u.amiga.statusdir = pia(p)->pddrb;
+	pia(p)->cra |= PIA_DDR;
+}
+
+static void mfc3_restore_state(struct parport *p, struct parport_state *s)
+{
+	pia(p)->pprb = s->u.amiga.data;
+	pia(p)->crb &= ~PIA_DDR;
+	pia(p)->pddrb = s->u.amiga.datadir;
+	pia(p)->crb |= PIA_DDR;
+	pia(p)->ppra = s->u.amiga.status;
+	pia(p)->cra &= ~PIA_DDR;
+	pia(p)->pddrb = s->u.amiga.statusdir;
+	pia(p)->cra |= PIA_DDR;
+}
+
+static void mfc3_enable_irq(struct parport *p)
+{
+	pia(p)->crb |= PIA_C1_ENABLE_IRQ;
+}
+
+static void mfc3_disable_irq(struct parport *p)
+{
+	pia(p)->crb &= ~PIA_C1_ENABLE_IRQ;
+}
+
+static void mfc3_inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void mfc3_dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static struct parport_operations pp_mfc3_ops = {
+	mfc3_write_data,
+	mfc3_read_data,
+
+	mfc3_write_control,
+	mfc3_read_control,
+	mfc3_frob_control,
+
+	NULL, /* write_econtrol */
+	NULL, /* read_econtrol */
+	NULL, /* frob_econtrol */
+
+	mfc3_write_status,
+	mfc3_read_status,
+
+	NULL, /* write fifo */
+	NULL, /* read fifo */
+
+	mfc3_change_mode,
+
+
+	mfc3_release_resources,
+	mfc3_claim_resources,
+
+
+	NULL, /* epp_write_data */
+	NULL, /* epp_read_data */
+	NULL, /* epp_write_addr */
+	NULL, /* epp_read_addr */
+	NULL, /* epp_check_timeout */
+
+	NULL, /* epp_write_block */
+	NULL, /* epp_read_block */
+
+	NULL, /* ecp_write_block */
+	NULL, /* ecp_read_block */
+
+	mfc3_init_state,
+	mfc3_save_state,
+	mfc3_restore_state,
+
+	mfc3_enable_irq,
+	mfc3_disable_irq,
+	NULL, /* change_irq */
+
+	mfc3_inc_use_count,
+	mfc3_dec_use_count
+};
+
+/* ----------- Initialisation code --------------------------------- */
+
+__initfunc(int parport_mfc3_init(void))
+{
+	struct parport *p;
+	int pias = 0;
+	struct pia *pp;
+	unsigned int key = 0;
+	const struct ConfigDev *cd;
+
+	if (MACH_IS_AMIGA) {
+		while ((key = zorro_find(ZORRO_PROD_BSC_MULTIFACE_III, 0, key))) {
+			cd = zorro_get_board(key);
+			pp = (struct pia *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+PIABASE));
+			if (pias < MAX_MFC) {
+				pp->crb = 0;
+				pp->pddrb = 255; /* all data pins output */
+				pp->crb = PIA_DDR|32|8;
+				dummy = pp->pddrb; /* reading clears interrupt */
+				pp->cra = 0;
+				pp->pddra = 0xe0; /* /RESET,  /DIR ,/AUTO-FEED output */
+				pp->cra = PIA_DDR;
+				pp->ppra = 0; /* reset printer */
+				udelay(10);
+				pp->ppra = 128;
+				if ((p = parport_register_port((unsigned long)pp,
+					IRQ_AMIGA_PORTS, PARPORT_DMA_NONE,
+					&pp_mfc3_ops))) {
+					this_port[pias++] = p;
+					printk(KERN_INFO "%s: Multiface III port using irq\n", p->name);
+					/* XXX: set operating mode */
+					parport_proc_register(p);
+					p->flags |= PARPORT_FLAG_COMA;
+					if (parport_probe_hook)
+						(*parport_probe_hook)(p);
+					zorro_config_board(key, 0);
+					p->private_data = (void *)key;
+				}
+			}
+		}
+	}
+	return pias;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return ! parport_mfc3_init();
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_MFC; i++)
+		if (this_port[i] != NULL) {
+			if (!(this_port[i]->flags & PARPORT_FLAG_COMA))
+				parport_quiesce(this_port[i]);
+			parport_proc_unregister(this_port[i]);
+			parport_unregister_port(this_port[i]);
+			zorro_unconfig_board((unsigned int)this_port[i]->private_data, 0);
+		}
+}
+#endif
+
+
--- ./drivers/misc/multiface.h.old	Sun Sep 20 14:43:19 1998
+++ ./drivers/misc/multiface.h	Sun Sep 20 14:43:07 1998
@@ -0,0 +1,20 @@
+#ifndef _MULTIFACE_H_
+#define _MULTIFACE_H_
+
+/*
+ * Defines for SerialMaster, Multiface Card II and Multiface Card III
+ * The addresses given below are offsets to the board base address
+ * 
+ * 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ */
+
+#define PIA_REG_PADWIDTH 255
+
+#define DUARTBASE 0x0000
+#define PITBASE   0x0100
+#define ROMBASE   0x0200
+#define PIABASE   0x4000
+
+#endif
+
--- ./include/linux/parport.h.old	Mon Aug 10 17:38:36 1998
+++ ./include/linux/parport.h	Sun Sep 27 14:01:10 1998
@@ -78,11 +78,20 @@
 	unsigned int ecr;
 };
 
+/* used by both parport_amiga and parport_mfc3 */
+struct amiga_parport_state {
+	unsigned char data;	/* ciaa.prb */
+	unsigned char datadir;	/* ciaa.ddrb */
+	unsigned char status;	/* ciab.pra & 7 */
+	unsigned char statusdir;/* ciab.ddrb & 7 */
+};
+
 struct parport_state {
 	union {
 		struct pc_parport_state pc;
 		/* ARC has no state. */
 		/* AX uses same state information as PC */
+		struct amiga_parport_state amiga;
 		void *misc; 
 	} u;
 };
@@ -123,7 +132,7 @@
 
 	void (*enable_irq)(struct parport *);
 	void (*disable_irq)(struct parport *);
-	int (*examine_irq)(struct parport *);
+	int (*change_irq)(struct parport *,int newirq);
 
 	void (*inc_use_count)(void);
 	void (*dec_use_count)(void);
@@ -293,6 +302,9 @@
 	parport_release(dev);
 	return parport_claim_or_block(dev);
 }
+
+/* irq helper function */
+extern void parport_intr_func(int irq, void *dev_id, struct pt_regs *regs);
 
 /* Flags used to identify what a device does. */
 #define PARPORT_DEV_TRAN	        0x0000  /* We're transient. */
--- ./include/linux/lp.h.old	Sun Sep 27 13:57:03 1998
+++ ./include/linux/lp.h	Sun Sep 27 15:02:48 1998
@@ -47,7 +47,12 @@
  * I'll initialize it to 0.
  */
 
+#if defined(__sparc__) || defined(__mc68000__)
+/* These machines need some extra delay */
+#define LP_INIT_WAIT 1
+#else
 #define LP_INIT_WAIT 0
+#endif
 
 /* This is the amount of time that the driver waits for the printer to
  * catch up when the printer's buffer appears to be filled.  If you

