MIPS cpu-speed detection using mc146818 clock.

Compute CPU speed(MHz) and loop multiplier for DELAY() based on
counting empty loop between mcclock ticks.  New global: cpu_mhz.
Change pmax/pmax/machdep.c to build baseboard model names from cpu_mhz.
Set  'cpuspeed' for more realistic DELAY() on mips3 models.

Mips CPU constants, testing, and calibration from D. Sean Davidson
<davidson@zk3.dec.com> and Simon Burge <simonb@telstra.com.au>.
This commit is contained in:
jonathan 1997-08-09 05:51:56 +00:00
parent 003ccf3b1c
commit 95a12ee943
4 changed files with 371 additions and 6 deletions

View File

@ -3,6 +3,7 @@ file arch/mips/mips/db_interface.c ddb
file arch/mips/mips/db_trace.c ddb
file arch/mips/mips/cpu_exec.c
file arch/mips/mips/mem.c
file arch/mips/mips/mips_mcclock.c clock # get CPU speed via mcclock
file arch/mips/mips/pmap.c
file arch/mips/mips/trap.c # interrupt, trap handlers
file arch/mips/mips/vm_machdep.c

View File

@ -0,0 +1,275 @@
/* $NetBSD: mips_mcclock.c,v 1.1 1997/08/09 05:51:57 jonathan Exp $ */
/*
* Copyright (c) 1997 Jonathan Stone (hereinafter referred to as the author)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Jonathan Stone for
* the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: mips_mcclock.c,v 1.1 1997/08/09 05:51:57 jonathan Exp $");
#include <sys/types.h>
#include <sys/systm.h>
#include <dev/ic/mc146818reg.h> /* clock rates */
#include <sys/param.h> /* guaranteed to get splXXX() */
#include <machine/clock_machdep.h>
#include <mips/cpu.h> /* CPUISMIPS3 */
#include <mips/mips/mips_mcclock.h>
#include <pmax/pmax/clockreg.h> /* XXX struct chiptime */
extern u_int mips_read_causereg __P((void));
unsigned mips_mc_cpuspeed __P((
volatile struct chiptime *mcaddr, int clockmask,
int (*tickpollfn) __P((volatile struct chiptime *mcclock_addr,
int clockmask)) ));
unsigned mips_mcclock_to_mhz __P((unsigned iters));
int mips_mcclock_tickloop __P((volatile struct chiptime *mcclock_addr,
int clockmask));
/*
* Estimate CPU cycle speed by counting cycles (acutally executions of a
* one-line loop) between two adjacent ticks of an mc146818 clock.
* Return loop iteration count so hand-calibrated MD code can
* estimate clock speed from cycles.
*
* Runs before CPU is attached (so we can print CPU speed) which is
* before the clock is attached, so we can't use the normal clock driver.
*/
unsigned
mips_mc_cpuspeed(mcclock_addr, clockmask, tickpollfn)
volatile struct chiptime *mcclock_addr;
int clockmask;
int (*tickpollfn) __P((volatile struct chiptime *mcclock_addr,
int clockmask));
{
register int s;
register int iters = 0;
int saved_rega, saved_regb;
/*
* Block all interrupts, including clock ticks.
*/
s = splhigh();
/*
* Enable periodic interrupst on the mc146818,
* and set it up for 256Hz (4ms) interrupts.
* Save any state we change so we can restore it on exit.
*/
saved_rega = mcclock_addr->rega;
saved_regb = mcclock_addr->regb;
#if 0
mcclock_addr->rega = (saved_rega & ~MC_BASE_RESET) | MC_RATE_256_Hz;
mcclock_addr->regb = MC_REGB_BINARY|MC_REGB_24HR|MC_REGB_PIE;
#else
mcclock_addr->rega = MC_BASE_32_KHz | MC_RATE_256_Hz;
mcclock_addr->regb = MC_REGB_BINARY|MC_REGB_24HR|MC_REGB_PIE| MC_REGB_SQWE;
#endif
/* count loop iterations between ticks */
iters = (*tickpollfn)(mcclock_addr, clockmask);
/* Restore mcclock registers */
mcclock_addr->rega = saved_rega;
mcclock_addr->regb = saved_regb;
splx(s);
/*
* Compute approximate CPU speed in MHz, and an
* appropriate base for DELAY() and delay(), from
* the number of completed iterations.
*/
cpu_mhz = mips_mcclock_to_mhz(iters);
#if defined(DEBUG) || defined(DIAGNOSTIC) || 1
printf("mcclock: iters %d computed MHz %d, instrs per usec=%d\n",
iters, cpu_mhz, cpuspeed);
#endif
return (iters);
}
/*
* Poll mcclock chip for the next tick interrupt and count
* instructions until the subsequent tick.
*
* XXX Assumes the mcclock chip has exclusive use of a CPU interrupt line.
* XXX Assumes bus access to clock registers is cheap (not a function call).
* MD alternatives must be used where this doesn't hold.
*/
int
mips_mcclock_tickloop(mcclock_addr, clockmask)
volatile struct chiptime *mcclock_addr;
int clockmask;
{
register int iters = 0;
register volatile int junk;
/* clear any old pending interrupts */
junk = mcclock_addr->regc;
junk++; junk++; junk++; junk++;
/* Poll clock interrupt, waiting for next tick to happen. */
while ((mips_read_causereg() & clockmask) == 0)
;
/* Ack the mc146818 interrupt caused by starting tick. */
junk = mcclock_addr->regc;
junk++; junk++; junk++; junk++;
/* Count loops until next tick-interrupt request occurs (4ms). */
if (CPUISMIPS3) {
while ((mips_read_causereg() & clockmask) == 0) {
__asm __volatile ("nop; nop; nop; nop");
iters++;
}
} else {
while ((mips_read_causereg() & clockmask) == 0)
iters++;
}
/* Ack the interrupt from the just-gone-off tick */
junk = mcclock_addr->regc;
return (iters);
}
/*
* Compute MHz and DELAY() constants using the default
* polling function.
*/
unsigned
mc_cpuspeed(mcclock_addr, clockmask)
volatile struct chiptime *mcclock_addr;
int clockmask;
{
return mips_mc_cpuspeed(mcclock_addr, clockmask,
mips_mcclock_tickloop);
}
/*
* mips_mcclock_to_mhz(iters) -- convert an mcclock cycles-per-tick count
* to a CPU speed in MHz.
*
* Side Effects:
* set the global variables "cpuspeed", used by DELAY() and delay()
* as an instructions-per-microsecond multiplier, to an value appropriate
* for the estimated clock speed.
*/
unsigned
mips_mcclock_to_mhz(unsigned iters)
{
register unsigned mhz = 0;
/* XXX KN01? */
/*
* Measured thresholds for Digital systems from Sean Davidson.
*
* r3000-core DECstations values fit to:
* iters per 4ms tick = 425 * MHz)
* instructions per mhz = kHz * 575
* with about 2 Mhz slop to allow for variation.
*/
#ifdef MIPS3
if (CPUISMIPS3) {
if (iters < 18100) {
/* error */
printf("mcclock loop count %d too low for r4000\n",
iters);
mhz = 45;
cpuspeed = 90; /* XXX */
} else if (iters < 20000) {
mhz = 50;
cpuspeed = 100; /* XXX */
} else if (iters < 24110) {
mhz = 60;
cpuspeed = 110; /* XXX */
} else if (iters < 27140) {
mhz = 67;
cpuspeed = 130; /* XXX */
} else if (iters < 30000) {
mhz = 75;
cpuspeed = 150; /* XXX */
}
}
#endif /* MIPS3 */
#ifdef MIPS1
if (!CPUISMIPS3) {
if (iters < 8800) {
mhz = 20;
cpuspeed = 11;
} else if (iters < 11300) {
mhz = 25;
cpuspeed = 13;
} else if (iters < 14000) {
mhz = 33;
cpuspeed = 19;
} else if (iters < 15000) {
mhz = 36;
cpuspeed = 21;
} else if (iters < 16000) {
mhz = 40;
cpuspeed = 23;
} else if (iters < 18800) {
mhz = 45;
cpuspeed = 25;
} else if (iters < 21000) {
mhz = 50;
cpuspeed = 29;
} else if (iters < 26000) {
mhz = 60;
cpuspeed = 35;
} else {
/* XXX */
mhz = 70;
cpuspeed = 40;
}
}
#endif /* MIPS1 */
return (mhz);
}

View File

@ -0,0 +1,51 @@
/* $NetBSD: mips_mcclock.h,v 1.1 1997/08/09 05:51:58 jonathan Exp $ */
/*
* Copyright (c) 1997 Jonathan Stone (hereinafter referred to as the author)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Jonathan Stone for
* the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _MIPS_MCCLOCK_H_
#define _MIPS_MCCLOCK_H_
/*
* Estimate CPU speed and multiplier for DELAY() by polling mcclock,
* couting iterations of an loop between successive ticks.
*/
struct chiptime;
unsigned mc_cpuspeed __P((volatile struct chiptime *mcaddr, int clockmask));
/*
* CPU speed in MHz, as estimated by mc_cpuspeed(). Read-only.
*/
extern int cpu_mhz;
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: machdep.c,v 1.93 1997/08/06 12:03:37 jonathan Exp $ */
/* $NetBSD: machdep.c,v 1.94 1997/08/09 05:51:59 jonathan Exp $ */
/*
* Copyright (c) 1988 University of Utah.
@ -85,6 +85,8 @@
#include <machine/pte.h>
#include <machine/autoconf.h>
#include <mips/locore.h> /* wbflush() */
#include <mips/mips/mips_mcclock.h> /* mclock CPU setimation */
#ifdef DDB
#include <mips/db_machdep.h>
#endif
@ -244,6 +246,8 @@ void kn02_enable_intr __P ((u_int slotno,
#ifdef DS5000_100
void kmin_enable_intr __P ((u_int slotno, int (*handler) (intr_arg_t sc),
intr_arg_t sc, int onoff));
void kmin_mcclock_cpuspeed __P((volatile struct chiptime *mcclock_addr,
int clockmask));
#endif /*DS5000_100*/
#ifdef DS5000_25
@ -286,7 +290,6 @@ struct proc nullproc; /* for use by switch_exit() */
/* locore callback-vector setup */
extern void mips_vector_init __P((void));
/*
* Do all the stuff that locore normally does before calling main().
* Process arguments passed to us by the prom monitor.
@ -508,6 +511,8 @@ mach_init(argc, argv, code, cv)
mcclock_addr = (volatile struct chiptime *)
MIPS_PHYS_TO_KSEG1(KN01_SYS_CLOCK);
mc_cpuspeed(mcclock_addr, MIPS_INT_MASK_3);
strcpy(cpu_model, "3100");
break;
#endif /* DS3100 */
@ -563,6 +568,7 @@ mach_init(argc, argv, code, cv)
MIPS_PHYS_TO_KSEG1(KN02_SYS_CLOCK);
}
mc_cpuspeed(mcclock_addr, MIPS_INT_MASK_1);
strcpy(cpu_model, "5000/200");
break;
#endif /* DS5000_200 */
@ -594,7 +600,7 @@ mach_init(argc, argv, code, cv)
Mach_splstatclock = splhigh;
mcclock_addr = (volatile struct chiptime *)
MIPS_PHYS_TO_KSEG1(KMIN_SYS_CLOCK);
kmin_mcclock_cpuspeed(mcclock_addr, MIPS_INT_MASK_3);
/*
* Initialize interrupts.
@ -607,7 +613,8 @@ mach_init(argc, argv, code, cv)
(u_int*)MIPS_PHYS_TO_KSEG1(KMIN_REG_TIMEOUT);
(*Mach_reset_addr) = 0;
strcpy(cpu_model, (CPUISMIPS3)? "5000/150": "5000/1xx");
sprintf(cpu_model, (CPUISMIPS3)? "5000/1%d": "5000/%d",
cpu_mhz);
/*
* The kmin memory hardware seems to wrap memory addresses
@ -654,6 +661,7 @@ mach_init(argc, argv, code, cv)
Mach_splstatclock = cpu_spl3;
mcclock_addr = (volatile struct chiptime *)
MIPS_PHYS_TO_KSEG1(XINE_SYS_CLOCK);
mc_cpuspeed(mcclock_addr, MIPS_INT_MASK_1);
/*
* Initialize interrupts.
@ -665,7 +673,7 @@ mach_init(argc, argv, code, cv)
(u_int*)MIPS_PHYS_TO_KSEG1(XINE_REG_TIMEOUT);
(*Mach_reset_addr) = 0;
strcpy(cpu_model, (CPUISMIPS3) ? "5000/50": "5000/25");
sprintf(cpu_model, "5000/%d", cpu_mhz);
break;
#endif /*DS5000_25*/
@ -699,6 +707,7 @@ mach_init(argc, argv, code, cv)
Mach_splstatclock = cpu_spl1;
mcclock_addr = (volatile struct chiptime *)
MIPS_PHYS_TO_KSEG1(KN03_SYS_CLOCK);
mc_cpuspeed(mcclock_addr, MIPS_INT_MASK_1);
asic_init(0);
/*
@ -714,7 +723,7 @@ mach_init(argc, argv, code, cv)
/* clear any memory errors from probes */
*Mach_reset_addr = 0;
strcpy(cpu_model, (CPUISMIPS3) ? "5000/260" : "5000/240");
sprintf(cpu_model, "5000/2%d", cpu_mhz);
break;
#endif /* DS5000_240 */
@ -1564,6 +1573,35 @@ kmin_enable_intr(slotno, handler, sc, on)
tc_slot_info[slotno].sc = 0;
}
}
/*
* Count instructions between 4ms mcclock interrupt requests,
* using the ioasic clock-interrupt-pending bit to determine
* when clock ticks occur.
* Set up iosiac to allow only clock interrupts, then
* call
*/
void
kmin_mcclock_cpuspeed(mcclock_addr, clockmask)
volatile struct chiptime *mcclock_addr;
int clockmask;
{
register volatile u_int * ioasic_intrmaskp =
(volatile u_int *)MIPS_PHYS_TO_KSEG1(KMIN_REG_IMSK);
register int saved_imask = *ioasic_intrmaskp;
/* Allow only clock interrupts through ioasic. */
*ioasic_intrmaskp = KMIN_INTR_CLOCK;
wbflush();
mc_cpuspeed(mcclock_addr, clockmask);
*ioasic_intrmaskp = saved_imask;
wbflush();
}
#endif /*DS5000_100*/