Subject: L68K: amiga-parport for 2.1.119
To: Jes.Sorensen@cern.ch (Jes Sorensen),
        linux-m68k@phil.uni-sb.de (Linux Liste), linux-parport@torque.net
Date: Tue, 1 Sep 1998 01:19:15 +0200 (MET DST)
From: Joerg Dorchain <dorchain@wirbel.com>
Sender: owner-linux-m68k@phil.uni-sb.de

Hi,

this is the patch for amiga-parport for 2.1.119. As I received no mails
wether printing works or not with this driver, I ask you again to please
test the driver and report the results (regardless wehter good or bad,
I'd like to hear anything!)
Although I don't promised it doesn't blow up your PC, printer or mind
;-), I'm still as sane as before and the printer still prints. So chances
can't be that bad for your system.

Joerg

--- ./arch/m68k/config.in.orig	Sun Aug 30 13:33:42 1998
+++ ./arch/m68k/config.in	Mon Aug 31 14:39:23 1998
@@ -102,6 +102,15 @@
   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
+    fi
+  fi
+fi
 endmenu
 
 source drivers/block/Config.in
@@ -257,9 +266,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.orig	Sun Aug 30 13:33:56 1998
+++ ./drivers/char/lp.c	Mon Aug 31 14:39:23 1998
@@ -94,7 +94,7 @@
 #include <linux/delay.h>
 
 #include <linux/parport.h>
-#undef LP_STATS
+#define LP_STATS
 #undef LP_NEED_CAREFUL
 #include <linux/lp.h>
 
@@ -126,7 +126,7 @@
 #define LP_READY(minor, status) ((status) & LP_PBUSY)
 #endif
 
-#undef LP_DEBUG
+#define LP_DEBUG
 #undef LP_READ_DEBUG
 
 /* --- parport support ----------------------------------------- */
@@ -211,7 +211,7 @@
 #endif
 	/* must wait before taking strobe high, and after taking strobe
 	   low, according spec.  Some printers need it, others don't. */
-#ifndef __sparc__
+#if !defined(__sparc__) && !defined(__mc68000__)
 	while (wait != LP_WAIT(minor)) /* FIXME: should be a udelay() */
 		wait++;
 #else
@@ -219,7 +219,7 @@
 #endif
 	/* control port takes strobe high */
 	w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
-#ifndef __sparc__
+#if !defined(__sparc__) && !defined(__mc68000__)
 	while (wait)			/* FIXME: should be a udelay() */
 		wait--;
 #else
@@ -316,6 +316,7 @@
 	unsigned long total_bytes_written = 0;
 	unsigned long bytes_written;
 	struct lp_struct *lp = &lp_table[minor];
+	unsigned long flags;
 
 	if (minor >= LP_NO)
 		return -ENXIO;
@@ -384,6 +385,11 @@
 					current->timeout = jiffies + LP_TIME(minor);
 					lp_schedule (minor);
 				} else {
+					/* Replace cli()/sti() by
+					  cli()/restore_flags.
+					  It's a must for other architectures
+					  and doesn't break i386 */
+					save_flags(flags);
 					cli();
 					if (LP_PREEMPTED(minor))
 					{
@@ -394,7 +400,7 @@
 						 * envinroment to avoid parport sharing
 						 * starvation.
 						 */
-						sti();
+						restore_flags(flags);
 						goto lp_polling;
 					}
 					if (!lp_table[minor].irq_detected)
@@ -402,7 +408,7 @@
 						current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
 						interruptible_sleep_on(&lp->wait_q);
 					}
-					sti();
+					restore_flags(flags);
 				}
 			}
 		}
--- ./drivers/misc/Makefile.orig	Mon Feb 23 21:26:49 1998
+++ ./drivers/misc/Makefile	Mon Aug 31 14:39:23 1998
@@ -37,6 +37,13 @@
       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
   LX_OBJS += parport_init.o
 else
   ifeq ($(CONFIG_PARPORT),m)
@@ -52,6 +59,9 @@
   endif
   ifeq ($(CONFIG_PARPORT_AX),m)
     M_OBJS += parport_ax.o
+  endif
+  ifeq ($(CONFIG_PARPORT_AMIGA),m)
+    M_OBJS += parport_amiga.o
   endif
 endif
 
--- ./drivers/misc/parport_amiga.c.orig	Mon Aug 31 14:39:23 1998
+++ ./drivers/misc/parport_amiga.c	Mon Aug 31 14:39:23 1998
@@ -0,0 +1,311 @@
+/* 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 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 & 0x10) /* XXX: What is INTEN? */
+		;
+	if (control & 0x08) /* XXX: What is SELECP? */
+		;
+	if (control & 0x04) /* INITP */
+		/* reset connected to cpu reset pin */;
+	if (control & 0x02) /* AUTOLF */
+		/* Not connected */;
+	if (control & 0x01) /* Strobe */
+		/* Handled only directly by hardware */;
+	return ret;
+}
+#endif
+
+static unsigned char control_amiga_to_pc(unsigned char control)
+{
+	return 0x1b;
+	/* 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 & 0x80) /* Busy */
+		ret &= ~1;
+	if (status & 0x40) /* Ack */
+		/* handled in hardware */;
+	if (status & 0x20) /* PaperOut */
+		ret |= 2;
+	if (status & 0x10) /* select */
+		ret |= 4;
+	if (status & 0x08) /* error */
+		/* not connected */;
+	return ret;
+}
+
+static unsigned char status_amiga_to_pc(unsigned char status)
+{
+	unsigned char ret = 0x80 + 0x40 + 0x08;
+
+	if (status & 1) /* Busy */
+		ret &= ~0x80;
+	if (status & 2) /* PaperOut */
+		ret |= 0x20;
+	if (status & 4) /* Selected */
+		ret |= 0x10;
+	/* 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*/
+}
+
+static void amiga_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* nothing to do */
+}
+
+static void amiga_release_resources(struct parport *p)
+{
+DPRINTK("realease_resources\n");
+	if (p->irq != PARPORT_IRQ_NONE)
+		free_irq(IRQ_AMIGA_CIAA_FLG, &ciaa.prb);
+}
+
+static int amiga_claim_resources(struct parport *p)
+{
+DPRINTK("claim_resources\n");
+	return request_irq(IRQ_AMIGA_CIAA_FLG, amiga_intr_func, 0, p->name, &ciaa.prb);
+}
+
+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 int amiga_examine_irq(struct parport *p)
+{
+	return 1; /* if an interrupt happened, we know it is ours */
+}
+
+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,
+	amiga_examine_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;
+		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)
+{
+	struct parport *p = parport_enumerate(), *next;
+
+	while (p) {
+		next = p->next;
+		if (p->ops == &pp_amiga_ops) {
+			if (!(p->flags & PARPORT_FLAG_COMA))
+				parport_quiesce(p);
+			parport_proc_unregister(p);
+			parport_unregister_port(p);
+		}
+		p = next;
+	}
+}
+#endif
+
+
--- ./drivers/misc/parport_init.c.orig	Thu Dec 11 06:23:33 1902
+++ ./drivers/misc/parport_init.c	Mon Aug 31 14:39:23 1998
@@ -122,6 +122,9 @@
 #ifdef CONFIG_PARPORT_AX
 	parport_ax_init();
 #endif
+#ifdef CONFIG_PARPORT_AMIGA
+	parport_amiga_init();
+#endif
 	return 0;
 }
 #endif
--- ./include/linux/parport.h.orig	Mon Aug 10 17:38:36 1998
+++ ./include/linux/parport.h	Mon Aug 31 14:47:23 1998
@@ -78,11 +78,19 @@
 	unsigned int ecr;
 };
 
+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;
 };
