Date: Wed, 10 Dec 1997 10:26:57 +0100 (MET)
From: Juergen Orschiedt <juergen.orschiedt@zx.basf-ag.de>
Subject: L68K: patch for atari_MFPser.c
To: linux-m68k@lists.linux-m68k.org
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: juergen.orschiedt@zx.basf-ag.de

Here's an enhancement for the atari_MFPser.[ch]

 o the Tx output is hold HIGH during disabled output. This avoids a
   glitch on the Tx-line when the Transmitter is enabled

 o auto-detect for MFP-speeders like RSVE, RSFI
   (thanks to Harun Scheutzow for the necessary code-fragments)

 o speed-dependend delay during baudrate-change to allow PLL's
   to lock before the first characters are send

If a speeder is not detected with your hardware, i need at least
the two 'detect_MFP_speeder'-lines from dmesg. You should also test
if the hsmoda-package (TOS) is working on your machine.

Hint:
  As far as i now Harun Scheutzow has additional RSFI available,
  which brings a 2048Bit FIFO and Baudrates up to 230kB to your
  machine. Works with _each_ MFP, independent of machine-type!

 
--- linux-2.1.64/drivers/char/atari_MFPser.c.old	Wed Dec  3 16:28:36 1997
+++ linux-2.1.64/drivers/char/atari_MFPser.c	Tue Dec  9 21:50:14 1997
@@ -4,6 +4,9 @@
  * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
  * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
  *
+ * Special thanks to Harun Scheutzow (developer of RSVE, RSFI, ST_ESCC and
+ * author of hsmoda-package) for the code to detect RSVE/RSFI.
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -29,6 +32,14 @@
  * used to implement Serial1 for the TT and the (not connected) MFP
  * port of the Falcon.
  *
+ * Juergen: changes based on Harun Scheutzows code
+ *   o added detection of RSVE, RSFI and possible PLL's
+ *   o set info->hub6 to identify speeder-hardware
+ *   o changed Tx output-level when transmitter is disabled
+ *   o no need for CONFIG_ATARI_MFPSER_EXT
+ *
+ *
+ *   o add delays for baudrate-setting to lock PLLs (RSFI, RSVE)
  */
 
 #include <linux/config.h>
@@ -49,7 +60,8 @@
 
 #include "atari_MFPser.h"
 
-
+#define RSFI_DEBUG		/* undefine to get rid of "detect_MFP_speeder: clock[x] = y" */
+#define RSFI_PLL_LOCK_DELAY	/* use delay to allow PLL settling */
 
 /***************************** Prototypes *****************************/
 
@@ -109,7 +121,6 @@
 	NULL
 };
 
-
   /* MFP Timer Modes divided by 2 (this already done in the BAUD_BASE 
    * The real 68901 prescaler factors are twice these values! 
    * prescaler_factor[] = {4, 10, 16, 50, 64, 100, 200}
@@ -119,8 +130,8 @@
   /* RSVE or RSSPEED will only recognize the 3 frequencies for 
    * 110, 134, 150 Baud, if the prescaler is 4 and the counter value does 
    * the rest. The divisors 350 and 256 can be built in multiple ways. 
-   * This driver tries to use the largest presscaler factor possible and 
-   * uses small counter values. TOS uses a presscaler factor of 4
+   * This driver tries to use the largest prescaler factor possible and 
+   * uses small counter values. TOS uses a prescaler factor of 4
    * ==> MFP_timer_mode = 2 ==> Index 0. Then RSVE replaces the clock 
    * correctly. Since the absolute frequencies don't have to be so accurate 
    * but the the prescaler factor has to be 4 we have to make sure that the 
@@ -131,7 +142,33 @@
    * the values by ignoring the lower 3 bits.
    */
 
-int MFP_baud_table[20] = { /* Divisors for standard speeds & RSVE */
+   /* Added support for RSFI
+    * RSFI is a hardware-FIFO with the following features:
+    *  o 2048bit Rx-FIFO
+    *  o baudrates: 38400, 57600, 76800, 115200, 153600, 230400
+    *    baudrate-selection and FIFO-enable is done by setting
+    *    the effective baudrate to 50 .. 200. In contrast to the
+    *    RSVE the clockselection is independend from the prescale-
+    *    factor. Instead, the MFP must be in x1 clockmode.
+    *  o the FIFO is only enabled when speeds are above 19k2
+    *
+    *  Relationship between MFP-baudrate and RSFI-baudrate
+    *   MFP		mode	RSFI
+    *	 50	x1	 76.8K *)
+    *	 75	x1	153.6K *)
+    *	110	x1	 38.4K
+    *	134	x1	 57.6K
+    *	150	x1	115.2K
+    *	200	x1	230.4K
+    *
+    * *) Non-standard baudrates, not supported.
+    *   
+    * We keep the speeder-type in info->hub6. This flag is unused on
+    * non-Intel architectures.
+    *
+    */
+
+int MFP_baud_table[22] = { /* Divisors for standard speeds, RSVE & RSFI */
   /* B0      */ 0,
   /* B50     */ 768,
   /* B75     */ 512,
@@ -147,11 +184,12 @@
   /* B4800   */ 8,
   /* B9600   */ 4,
   /* B19200  */ 2,
-  /* B38400  */ 348,  /*  38.4K with RSVE, prescaler 4 = MFP_mode 2 */
-  /* B57600  */ 286,  /*  57.6K with RSVE, prescaler 4 = MFP_mode 2 */
-  /* B115200 */ 258,  /* 115.2K with RSVE, prescaler 4 = MFP_mode 2 */
-  /* B230400 */ 0,    /* invalid */
-  /* B460800 */ 0     /* invalid */
+  /* B38400  */ 348,  /*  38.4K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* B57600  */ 286,  /*  57.6K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* B115200 */ 258,  /* 115.2K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */
+  /* --------------- the following values are ignored in RSVE-mode */
+  /* B230400 */ 192,  /* 230.4K with RSFI */
+  /* B460800 */ 0,    /* illegal */
 };
 
 
@@ -203,11 +241,176 @@
 	return( nr > 0 ? 0 : -ENODEV );
 }
 
+static void __inline__ set_timer_D(struct MFP *thismfp, int baud, int prescale) {
+/* set timer d to given value, prescale 4
+ * allow PLL-settling (3 bit-times)
+ */
+
+    int count;
+
+    __asm__ __volatile__ (
+	"	andb	#0xf8, 0x1c(%2)	\t\n"	/* disable timer D */
+	"	moveb	%3, 0x24(%2)	\t\n"	/* preset baudrate */
+	"	orb	%4, 0x1c(%2)	\t\n"	/* enable timer D, prescale N */
+	"1:	moveb	#0xef, 0x0c(%2)	\t\n"	/* clear pending int D */
+	"2:	btst	#4, 0x0c(%2)	\t\n"	/* pending int D? */
+	"	beq	2b		\t\n"	/*   no: wait... */
+	"	subq	#1, %0		\t\n"
+	"	bne	1b		\t\n"	
+	: "=d"(count)
+	: "0" (count=6), "a"(thismfp), "d"(baud), "d"(prescale)
+    );
+}
+
+static int detect_MFP_speeder(struct MFP *currMFP) {
+/* try to autodetect RSVE, RSFI or similiar RS232 speeders
+ *
+ *  (c) Harun Scheutzow 
+ *	    developer of RSVE, RSFI, ST_ESCC and author of hsmoda-package
+ *
+ *  integrated by Juergen Orschiedt
+ *
+ * noise-free detection of Tx/Rx baudrate
+ *  - set MFP to loopback, syncmode, 8bpc, syncchar=0xff
+ *  - enable transmitter and measure time between syncdetect
+ * depending on the relationship between measured time and
+ * timer-d setting we can tell which (if any) speeder we have.
+ *
+ * returncodes:
+ *	0	something wrong (1200 too slow)
+ *	1	no speeder detected
+ *	2	RSVE detected
+ *	3	RSFI detected
+ *	4	PLL or fixed Baudrate (Hardware-hack)
+ */
+
+	int count, speeder;
+	unsigned int flags;
+	int imra, imrb;
+
+	/* prepare IRQ registers for measurement */
+
+	save_flags(flags);
+	cli();
+
+	imra = currMFP->int_mk_a;
+	imrb = currMFP->int_mk_b;
+
+	currMFP->int_mk_a = imra & 0xe1;	/* mask off all Rx/Tx ints */
+	currMFP->int_mk_b = imrb & 0xef;	/* disable timer d int in IMRB */
+	currMFP->int_en_b |= 0x10;		/* enable in IERB (to see pending ints) */
+	restore_flags(flags);
+
+	(void)currMFP->par_dt_reg;		/* consume some cycles */
+	(void)currMFP->par_dt_reg;
+
+	/* initialize MFP */
+	currMFP->rcv_stat = 0x00;		/* disable Rx */
+	currMFP->trn_stat = TSR_SOMODE_HIGH;	/* disable Tx, output-level high */
+	currMFP->sync_char= 0xff;		/* syncchar = 0xff */
+	currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_SYNC_MODE | UCR_CHSIZE_8);
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_LOOP); 
+
+	/* look at 1200 baud setting (== effective 19200 in syncmode) */
+	set_timer_D(currMFP, 0x10, 0x01);
+	save_flags(flags);
+	cli();
+
+	/* check for fixed speed / bad speed */
+	__asm__ __volatile__ (
+		"	moveb	#0x1, 0x2a(%2)	\t\n"	/* currMFP->rcv_stat = RSR_RX_ENAB */
+		"1:	addql	#1, %0		\t\n"	/* count++; 			*/
+		"	moveb	#0xef, 0x0c(%2)	\t\n"	/* currMFP->int_pn_b &= 0xef;	*/
+		"2:	btst	#4, 0x0c(%2)	\t\n"	/* timer D toggle? */
+		"	bne	1b		\t\n"	/*   increment timer D zero-count */
+		"	btst	#3, 0x2a(%2)	\t\n"	/* currMFP->rcv_stat & RSR_SYNC_SEARCH */
+		"	bne	3f		\t\n"	/*   finish	*/
+		"	cmp	#22, %0		\t\n"	/* max. wait (= 11 bittimes) reached? */
+		"	bcs	2b		\t\n"	/*   no - again */
+		"3:				\n\t"
+		: "=d"(count)
+		: "0"(count=-1), "a"(currMFP)	
+	);
+	restore_flags(flags);
+
+	/* for RSxx or standard MFP we have 8 bittimes (count=16) */
+#ifdef	RSFI_DEBUG
+	printk(KERN_INFO "    detect_MFP_speeder: count[1200]=%d\n", count);
+#endif
+
+	if (count < 10)	
+		speeder = MFP_WITH_PLL;		/* less than 5 bittimes: primitive speeder */
+	else
+	if (count >22)
+		speeder = MFP_WITH_WEIRED_CLOCK;	/* something wrong - too slow! */
+	else {
+		/* 1200 baud is working, we neither have fixed clock nor simple PLL */ 
+		set_timer_D(currMFP, 0xaf, 0x01);
+		save_flags(flags);
+		cli();
+
+		/* check for RSxx or Standard MFP with 110 baud
+		 * timer D toggles each 290us
+		 *	      bps	sync char count
+		 * RSVE:    614400	 22.33		 13uS
+		 * RSFI:     38400	  1.39		208us
+		 * Standard:  1720	  0.06
+		 * 
+		 */ 
+		__asm__ __volatile__ (
+			"	moveb	#0xef, 0x0c(%2)	\t\n"	/* clear IRQ pending D */
+			"9:	btst	#4, 0x0c(%2)	\t\n"	/* syncronize timer D */
+			"	beq	9b		\t\n"
+			"	moveb	#0xef, 0x0c(%2)	\t\n"	/* clear IRQ pending D */
+			"1:	moveb	#0, 0x2a(%2)	\t\n"	/* disable Rx */
+			"	addql	#1, %0		\t\n"	/* count++ */
+			"	tstb	(%2)		\t\n"	/* consume time */
+			"	moveb	#1, 0x2a(%2)	\t\n"	/* enable Rx */
+			"	nop			\t\n"
+			"2:	btst	#3, 0x2a(%2)	\t\n"	/* sync char detect? */
+			"	bne	1b		\t\n"	/*   yes: increment sync-count */
+			"	btst	#4, 0x0c(%2)	\t\n"	/* timer D expired? */
+			"	beq	2b		\t\n"	/*   no: test again */
+			: "=d"(count)
+			: "0" (count=-1), "a"(currMFP)
+		);
+
+#ifdef RSFI_DEBUG
+		printk(KERN_INFO "    detect_MFP_speeder: count[110]=%d\n", count);
+#endif
+
+		if (count < 1) speeder = MFP_STANDARD;	/* no speeder detected */
+		else
+			if (count > 4) speeder = MFP_WITH_RSVE;
+		else
+			speeder = MFP_WITH_RSFI;
+	}
+
+	restore_flags(flags);
+	currMFP->rcv_stat = 0x00;		/* disable Rx */
+	currMFP->trn_stat = TSR_SOMODE_HIGH;	/* disable Tx, output-level high */
+	currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_ASYNC_2 | UCR_CHSIZE_8);
+	currMFP->int_mk_a = imra;
+	currMFP->int_mk_b = imrb;
+	currMFP->int_en_b &= 0xef;
+	currMFP->int_pn_a &= 0xe1;		/* mask off pending Rx/Tx */
+	currMFP->int_pn_b &= 0xef;		/* mask off pending Timer D */
+	return speeder;
+
+}
 
 static void MFPser_init_port( struct async_struct *info, int type, int tt_flag)
 {
 	INIT_currMFP(info);
-	
+	int speeder;
+	static char *speeder_name[]= {"", "", "PLL or fixed clock", "RSVE", "RSFI" };
+
+	/* look for possible speeders */
+	info->hub6 = speeder = detect_MFP_speeder((struct MFP *)currMFP);
+	if (speeder > MFP_STANDARD)
+	    printk(KERN_INFO "ttyS%d: Detected %s extension\n",
+		   info->line, speeder_name[speeder]);
+
 	/* set ISRs, but don't enable interrupts yet (done in init());
 	 * all ints are choosen of type FAST, and they're really quite fast.
 	 * Furthermore, we have to account for the fact that these are three ints,
@@ -247,7 +450,8 @@
   	info->baud_base = MFP_BAUD_BASE;
 
 	currMFP->rcv_stat  = 0;	/* disable Rx */
-	currMFP->trn_stat  = 0;	/* disable Tx */
+	currMFP->trn_stat  = TSR_SOMODE_HIGH;	/* disable Tx */
+
 }
 
 
@@ -360,13 +564,13 @@
 
 	/* base value for UCR */
 	currMFP->usart_ctr = (UCR_PARITY_OFF | UCR_ASYNC_1 |
-						  UCR_CHSIZE_8 | UCR_PREDIV);
+			      UCR_CHSIZE_8 | UCR_PREDIV);
 
 	/* enable Rx and clear any error conditions */
 	currMFP->rcv_stat = RSR_RX_ENAB;
 
 	/* enable Tx */
-	currMFP->trn_stat = TSR_TX_ENAB;
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH);
 
 	/* enable Rx, RxErr and Tx interrupts */
 	currMFP->int_en_a |= 0x1c;
@@ -413,7 +617,7 @@
 static void MFPser_deinit( struct async_struct *info, int leave_dtr )
 {
 	INIT_currMFP(info);
-
+	
 	/* disable Rx, RxErr and Tx interrupts */
 	currMFP->int_en_a &= ~0x1c;
 
@@ -424,7 +628,7 @@
 
 	/* disable Rx and Tx */
 	currMFP->rcv_stat = 0;
-	currMFP->trn_stat = 0;
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
 
 	/* wait for last byte to be completely shifted out */
 	while( !(currMFP->trn_stat & TSR_LAST_BYTE_SENT) )
@@ -514,9 +718,11 @@
 		if (baud < 1 || baud > 4)
 			info->tty->termios->c_cflag &= ~CBAUDEX;
 		else
-			baud += 15;
+			if (info->hub6 > MFP_WITH_PLL)	/* speeder detected? */
+			    baud += 15;
 	}
-	if (baud == 15) {
+	if ((info->hub6 > MFP_WITH_PLL) && (baud == 15)) {
+		/* only for speeders... */
 		switch (aflags) {
 		case ASYNC_SPD_HI:
 			baud += 1;  /* 134 Baud, with RSVE =  57600 */
@@ -525,7 +731,7 @@
 			baud += 2;  /* 150 Baud, with RSVE = 115200 */
 			break;
 		case ASYNC_SPD_SHI:
-			baud += 3;
+			baud += 3;	/* with RSFI: 230400 Baud */
 			break;
 		case ASYNC_SPD_WARP:
 			baud += 4;
@@ -536,12 +742,10 @@
 		}
 	}
 	if (!div) {
-#ifdef CONFIG_ATARI_MFPSER_EXT
-		/* Maximum MFP speed is 115200 */
-		if (baud > 17) baud = 17;
-#else
-		if (baud > 14) baud = 14;
-#endif
+		/* max. tableentries depending on speeder type */
+		static int maxbaud[] = { 14, 14, 14, 17, 18};
+		if (baud > maxbaud[info->hub6])
+			baud = maxbaud[info->hub6];
 		div = MFP_baud_table[baud];
 	}
 
@@ -567,27 +771,37 @@
 	cli();
 	/* disable Rx and Tx while changing parameters */
 	currMFP->rcv_stat = 0;
-	currMFP->trn_stat = 0;
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
 
+#ifdef RSFI_PLL_LOCK_DELAY
+	currMFP->int_en_b |= 0x10;
+	set_timer_D(currMFP, timer_val, timer_mode+1);
+	currMFP->int_en_b &= 0xef;
+#else
 	/* stop timer D to set new timer value immediatly after re-enabling */
 	currMFP->tim_ct_cd &= ~0x07;
 	currMFP->tim_dt_d = timer_val;
 	currMFP->tim_ct_cd |= (timer_mode+1);
+#endif
+	{
+	    unsigned shadow_ctr;
 
-	currMFP->usart_ctr =
-		( (parity & PARENB) ?
-		      ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) :
-		      UCR_PARITY_OFF ) |
-		( chsize == CS5 ? UCR_CHSIZE_5 :
-		  chsize == CS6 ? UCR_CHSIZE_6 :
-		  chsize == CS7 ? UCR_CHSIZE_7 :
-						  UCR_CHSIZE_8 ) |
-		( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 ) |
-		UCR_PREDIV;
+	    shadow_ctr = ((parity & PARENB) ?
+			 ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) :
+			 UCR_PARITY_OFF ) |
+			    ( chsize == CS5 ? UCR_CHSIZE_5 :
+			      chsize == CS6 ? UCR_CHSIZE_6 :
+			      chsize == CS7 ? UCR_CHSIZE_7 : UCR_CHSIZE_8 ) |
+			( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 );
+
+	    if ((baud < 15) || (info->hub6 != MFP_WITH_RSFI))
+		shadow_ctr |= UCR_PREDIV;
+	    currMFP->usart_ctr = shadow_ctr;
+	}
 
 	/* re-enable Rx and Tx */
 	currMFP->rcv_stat = RSR_RX_ENAB;
-	currMFP->trn_stat = TSR_TX_ENAB;
+	currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH);
 	restore_flags (ipl);
 }
 
@@ -703,7 +917,7 @@
 	/* disable receiver */
 	currMFP->rcv_stat = 0;
 	/* disable transmitter */
-	currMFP->trn_stat = 0;
+	currMFP->trn_stat = TSR_SOMODE_HIGH;
 }
 
 static int MFPser_trans_empty (struct async_struct *info)
--- linux-2.1.64/drivers/char/atari_MFPser.h.old	Tue Dec  2 16:05:03 1997
+++ linux-2.1.64/drivers/char/atari_MFPser.h	Mon Dec  8 22:48:49 1997
@@ -80,6 +80,13 @@
 #define	GPIP_CTS			0x04
 #define	GPIP_RI				0x40
 
+/* MFP speeders */
+#define	MFP_WITH_WEIRED_CLOCK	0x00
+#define	MFP_STANDARD		0x01
+#define MFP_WITH_PLL		0x02
+#define MFP_WITH_RSVE		0x03
+#define MFP_WITH_RSFI		0x04
+
 
 /* Convenience routine to access RTS and DTR in the Soundchip: It sets
  * the register to (oldvalue & mask) if mask is negative or (oldvalue
   


