Module Name:    src
Committed By:   phx
Date:           Sat Dec 12 13:10:36 UTC 2009

Modified Files:
        src/sys/arch/amiga/amiga: amiga_init.c locore.s
        src/sys/arch/amiga/dev: clock.c kbd.c

Log Message:
Reverted the CIA-timer based delay() to the pre-5.0 method of a calibrated
delay loop.
This fixes keyboard handshaking problems with some A1200 models since 5.0
and restores the precision for short delays on DraCo systems (the QuickLogic
timer has only a seventh of the CIA precision).
Changed the keyboard handshaking delay from 2000 back to 200ms, although
even the recommended 85ms were successfully tested on the most problematic
A1200 keyboards.
All those changes were tested on an A3000 and A1200 with 68060/50 CPU, and
previously discussed on the port-amiga ML.


To generate a diff of this commit:
cvs rdiff -u -r1.117 -r1.118 src/sys/arch/amiga/amiga/amiga_init.c
cvs rdiff -u -r1.148 -r1.149 src/sys/arch/amiga/amiga/locore.s
cvs rdiff -u -r1.50 -r1.51 src/sys/arch/amiga/dev/clock.c
cvs rdiff -u -r1.52 -r1.53 src/sys/arch/amiga/dev/kbd.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amiga/amiga/amiga_init.c
diff -u src/sys/arch/amiga/amiga/amiga_init.c:1.117 src/sys/arch/amiga/amiga/amiga_init.c:1.118
--- src/sys/arch/amiga/amiga/amiga_init.c:1.117	Fri Dec 11 22:23:08 2009
+++ src/sys/arch/amiga/amiga/amiga_init.c	Sat Dec 12 13:10:36 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: amiga_init.c,v 1.117 2009/12/11 22:23:08 tsutsui Exp $	*/
+/*	$NetBSD: amiga_init.c,v 1.118 2009/12/12 13:10:36 phx Exp $	*/
 
 /*
  * Copyright (c) 1994 Michael L. Hitch
@@ -36,7 +36,7 @@
 #include "opt_devreload.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.117 2009/12/11 22:23:08 tsutsui Exp $");
+__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.118 2009/12/12 13:10:36 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -765,6 +765,7 @@
 void
 start_c_finish(void)
 {
+	extern u_int32_t delaydivisor;
 #ifdef	P5PPC68KBOARD
         struct cfdev *cdp, *ecdp;
 #endif
@@ -895,6 +896,21 @@
 			}
         }
 #endif
+	/*
+	 * preliminary delay divisor value
+	 */
+
+	if (machineid & AMIGA_68060)
+		delaydivisor = (1024 * 1) / 80;	/* 80 MHz 68060 w. BTC */
+
+	else if (machineid & AMIGA_68040)
+		delaydivisor = (1024 * 3) / 40;	/* 40 MHz 68040 */
+
+	else if (machineid & AMIGA_68030)
+		delaydivisor = (1024 * 8) / 50;	/* 50 MHz 68030 */
+
+	else
+		delaydivisor = (1024 * 8) / 33; /* 33 MHz 68020 */
 }
 
 void

Index: src/sys/arch/amiga/amiga/locore.s
diff -u src/sys/arch/amiga/amiga/locore.s:1.148 src/sys/arch/amiga/amiga/locore.s:1.149
--- src/sys/arch/amiga/amiga/locore.s:1.148	Fri Dec  4 17:28:54 2009
+++ src/sys/arch/amiga/amiga/locore.s	Sat Dec 12 13:10:36 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: locore.s,v 1.148 2009/12/04 17:28:54 tsutsui Exp $	*/
+/*	$NetBSD: locore.s,v 1.149 2009/12/12 13:10:36 phx Exp $	*/
 
 /*
  * Copyright (c) 1980, 1990 The Regents of the University of California.
@@ -1573,6 +1573,16 @@
 	.align 2
 #endif
 	nop
+ENTRY_NOPROFILE(delay)
+ENTRY_NOPROFILE(DELAY)
+	movql #10,%d1		| 2 +2
+	movl %sp@(4),%d0	| 4 +4
+	lsll %d1,%d0		| 8 +2
+	movl _C_LABEL(delaydivisor),%d1	| A +6
+Ldelay:				| longword aligned again.
+	subl %d1,%d0
+	jcc Ldelay
+	rts
 
 #ifdef M68060
 ENTRY_NOPROFILE(intemu60)
@@ -1603,7 +1613,11 @@
 	.long	FPU_NONE
 GLOBAL(protorp)
 	.long	0x80000002,0	| prototype root pointer
-
+GLOBAL(delaydivisor)
+	.long	12		| should be enough for 80 MHz 68060
+				| will be adapted to other CPUs in
+				| start_c_cleanup and calibrated
+				| at clock attach time.
 #ifdef DEBUG
 ASGLOBAL(fulltflush)
 	.long	0

Index: src/sys/arch/amiga/dev/clock.c
diff -u src/sys/arch/amiga/dev/clock.c:1.50 src/sys/arch/amiga/dev/clock.c:1.51
--- src/sys/arch/amiga/dev/clock.c:1.50	Fri Sep 11 19:43:08 2009
+++ src/sys/arch/amiga/dev/clock.c	Sat Dec 12 13:10:36 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: clock.c,v 1.50 2009/09/11 19:43:08 phx Exp $ */
+/*	$NetBSD: clock.c,v 1.51 2009/12/12 13:10:36 phx Exp $ */
 
 /*
  * Copyright (c) 1982, 1990 The Regents of the University of California.
@@ -77,7 +77,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.50 2009/09/11 19:43:08 phx Exp $");
+__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.51 2009/12/12 13:10:36 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -100,26 +100,6 @@
 #include <sys/PROF.h>
 #endif
 
-/* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz.
-   We're using a 100 Hz clock. */
-int amiga_clk_interval;
-int eclockfreq;
-unsigned int fast_delay_limit;
-struct CIA *clockcia;
-
-static u_int clk_getcounter(struct timecounter *);
-
-static struct timecounter clk_timecounter = {
-	clk_getcounter,	/* get_timecount */
-	0,		/* no poll_pps */
-	~0u,		/* counter_mask */
-	0,		/* frequency */
-	"clock",	/* name, overriden later */
-	100,		/* quality */
-	NULL,		/* prev */
-	NULL,		/* next */
-};
-
 /*
  * Machine-dependent clock routines.
  *
@@ -140,6 +120,26 @@
 int clockmatch(struct device *, struct cfdata *, void *);
 void clockattach(struct device *, struct device *, void *);
 void cpu_initclocks(void);
+static void calibrate_delay(struct device *);
+
+/* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz.
+   We're using a 100 Hz clock. */
+int amiga_clk_interval;
+int eclockfreq;
+struct CIA *clockcia;
+
+static u_int clk_getcounter(struct timecounter *);
+
+static struct timecounter clk_timecounter = {
+	clk_getcounter,	/* get_timecount */
+	0,		/* no poll_pps */
+	~0u,		/* counter_mask */
+	0,		/* frequency */
+	"clock",	/* name, overriden later */
+	100,		/* quality */
+	NULL,		/* prev */
+	NULL,		/* next */
+};
 
 CFATTACH_DECL(clock, sizeof(struct device),
     clockmatch, clockattach, NULL, NULL);
@@ -160,21 +160,20 @@
 {
 	const char *clockchip;
 	unsigned short interval;
+	int chipfreq;
 #ifdef DRACO
 	u_char dracorev;
 #endif
 
-#ifdef DRACO
-	dracorev = is_draco();
-#endif
-
 	if (eclockfreq == 0)
 		eclockfreq = 715909;	/* guess NTSC */
 
+	chipfreq = eclockfreq;
+
 #ifdef DRACO
+	dracorev = is_draco();
 	if (dracorev >= 4) {
-		if (amiga_clk_interval == 0)	/* Only do this 1st time */
-			eclockfreq /= 7;
+		chipfreq = eclockfreq / 7;
 		clockchip = "QuickLogic";
 	} else if (dracorev) {
 		clockcia = (struct CIA *)CIAAbase;
@@ -186,16 +185,14 @@
 		clockchip = "CIA B";
 	}
 
-	amiga_clk_interval = (eclockfreq / hz);
-
-	clk_timecounter.tc_name = clockchip;
-	clk_timecounter.tc_frequency = eclockfreq;
-
-	fast_delay_limit = UINT_MAX / amiga_clk_interval;
+	amiga_clk_interval = chipfreq / hz;
 
 	if (dp != NULL) {	/* real autoconfig? */
 		printf(": %s system hz %d hardware hz %d\n", clockchip, hz,
-		    eclockfreq);
+		    chipfreq);
+
+		clk_timecounter.tc_name = clockchip;
+		clk_timecounter.tc_frequency = chipfreq;
 		tc_init(&clk_timecounter);
 	}
 
@@ -209,6 +206,8 @@
 		draco_ioct->io_timerlo = amiga_clk_interval & 0xff;
 		draco_ioct->io_timerhi = amiga_clk_interval >> 8;
 
+		calibrate_delay(dp);
+
 		return;
 	}
 #endif
@@ -222,7 +221,6 @@
 	/*
 	 * load interval into registers.
          * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz
-	 * supprort for PAL WHEN?!?! XXX
 	 */
 	interval = amiga_clk_interval - 1;
 
@@ -235,6 +233,8 @@
 	 * start timer A in continuous mode
 	 */
 	clockcia->cra = (clockcia->cra & 0xc0) | 1;
+
+	calibrate_delay(dp);
 }
 
 void
@@ -280,7 +280,7 @@
 }
 
 /*
- * Returns ticks  since last recorded clock "tick"
+ * Returns ticks since last recorded clock "tick"
  * (i.e. clock interrupt).
  */
 static u_int
@@ -326,30 +326,88 @@
 static u_int
 clk_getcounter(struct timecounter *tc)
 {
-	static int last_hardclock_ticks;
-	static u_int last_clock_tick = 0;
-	int old_hardclock_ticks;
-	u_int clock_tick;
+	static int prev_hardclock;
+	static u_int prev_counter;
+	int cur_hardclock;
+	u_int counter;
 
 	do {
-		old_hardclock_ticks = hardclock_ticks;
-		clock_tick = clk_gettick();
-	} while (old_hardclock_ticks != hardclock_ticks);
+		cur_hardclock = hardclock_ticks;
+		counter = clk_gettick();
+	} while (cur_hardclock != hardclock_ticks);
 
 	/*
 	 * Handle the situation of a wrapped interval counter, while
 	 * the hardclock() interrupt was not yet executed to update
 	 * hardclock_ticks.
 	 */
-	if (last_hardclock_ticks > old_hardclock_ticks)
-		old_hardclock_ticks = last_hardclock_ticks;
-	if (clock_tick < last_clock_tick &&
-	    old_hardclock_ticks == last_hardclock_ticks)
-		old_hardclock_ticks++;
-	last_hardclock_ticks = old_hardclock_ticks;
-	last_clock_tick = clock_tick;
+	if (cur_hardclock < prev_hardclock)
+		cur_hardclock = prev_hardclock;
+	if (counter < prev_counter && cur_hardclock == prev_hardclock)
+		cur_hardclock++;
+
+	prev_hardclock = cur_hardclock;
+	prev_counter = counter;
+
+	return cur_hardclock * amiga_clk_interval + counter;
+}
+
+/*
+ * Calibrate delay loop.
+ * We use two iterations because we don't have enough bits to do a factor of
+ * 8 with better than 1%.
+ *
+ * XXX Note that we MUST stay below 1 tick if using clk_gettick(), even for
+ * underestimated values of delaydivisor.
+ *
+ * XXX the "ns" below is only correct for a shift of 10 bits, and even then
+ * off by 2.4%
+ */
+static void
+calibrate_delay(struct device *dp)
+{
+	unsigned long t1, t2;
+	extern u_int32_t delaydivisor;
+		/* XXX this should be defined elsewhere */
 
-	return old_hardclock_ticks * amiga_clk_interval + clock_tick;
+	if (dp)
+		printf("Calibrating delay loop... ");
+
+	do {
+		t1 = clk_gettick();
+		delay(1024);
+		t2 = clk_gettick();
+	} while (t2 <= t1);
+	t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz);
+	delaydivisor = (delaydivisor * t2 + 1023) >> 10;
+#ifdef DEBUG
+	if (dp)
+		printf("\ndiff %ld us, new divisor %u/1024 us\n", t2,
+		    delaydivisor);
+	do {
+		t1 = clk_gettick();
+		delay(1024);
+		t2 = clk_gettick();
+	} while (t2 <= t1);
+	t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz);
+	delaydivisor = (delaydivisor * t2 + 1023) >> 10;
+	if (dp)
+		printf("diff %ld us, new divisor %u/1024 us\n", t2,
+		    delaydivisor);
+#endif
+	do {
+		t1 = clk_gettick();
+		delay(1024);
+		t2 = clk_gettick();
+	} while (t2 <= t1);
+	t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz);
+	delaydivisor = (delaydivisor * t2 + 1023) >> 10;
+#ifdef DEBUG
+	if (dp)
+		printf("diff %ld us, new divisor ", t2);
+#endif
+	if (dp)
+		printf("%u/1024 us\n", delaydivisor);
 }
 
 #if notyet
@@ -652,51 +710,3 @@
 }
 #endif
 #endif
-
-void
-delay(unsigned int n)
-{
-	unsigned int cur_tick, initial_tick;
-	int remaining;
-
-	/*
-	 * Read the counter first, so that the rest of the setup overhead is
-	 * counted.
-	 */
-	initial_tick = clk_gettick();
-
-	if (amiga_clk_interval == 0) {
-		/*
-		 * Clock is not initialised yet,
-		 * so just do some ad-hoc loop.
-		 */
-		static uint32_t dummy;
-
-		n *= 4;
-		while (n--)
-			dummy *= eclockfreq;
-		return;
-	}
-
-	if (n <= fast_delay_limit) {
-		/*
-		 * For unsigned arithmetic, division can be replaced with
-		 * multiplication with the inverse and a shift.
-		 */
-		remaining = n * eclockfreq / 1000000;
-	} else {
-		/* This is a very long delay.
-		 * Being slow here doesn't matter.
-		 */
-		remaining = (unsigned long long) n * eclockfreq / 1000000;
-	}
-
-	while (remaining > 0) {
-		cur_tick = clk_gettick();
-		if (cur_tick > initial_tick)
-			remaining -= amiga_clk_interval - (cur_tick - initial_tick);
-		else
-			remaining -= initial_tick - cur_tick;
-		initial_tick = cur_tick;
-	}
-}

Index: src/sys/arch/amiga/dev/kbd.c
diff -u src/sys/arch/amiga/dev/kbd.c:1.52 src/sys/arch/amiga/dev/kbd.c:1.53
--- src/sys/arch/amiga/dev/kbd.c:1.52	Sat Mar 14 15:36:01 2009
+++ src/sys/arch/amiga/dev/kbd.c	Sat Dec 12 13:10:36 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: kbd.c,v 1.52 2009/03/14 15:36:01 dsl Exp $ */
+/*	$NetBSD: kbd.c,v 1.53 2009/12/12 13:10:36 phx Exp $ */
 
 /*
  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kbd.c,v 1.52 2009/03/14 15:36:01 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kbd.c,v 1.53 2009/12/12 13:10:36 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -560,7 +560,7 @@
 	}
 #endif
 	/* wait 200 microseconds (for bloody Cherry keyboards..) */
-	DELAY(2000);			/* fudge delay a bit for some keyboards */
+	DELAY(200);			/* fudge delay a bit for some keyboards */
 	ciaa.cra &= ~(1 << 6);
 
 	/* process the character */
@@ -652,7 +652,7 @@
 	ciaa.cra |= (1 << 6);	/* serial line output */
 	ciaa.sdr = 0xff;	/* ack */
 	/* wait 200 microseconds */
-	DELAY(2000);	/* XXXX only works as long as DELAY doesn't
+	DELAY(200);	/* XXXX only works as long as DELAY doesn't
 			 * use a timer and waits.. */
 	ciaa.cra &= ~(1 << 6);
 	ciaa.sdr = in;

Reply via email to