/* -*- Fundamental -*- keep Emacs from f***ing up the formatting */
/*
 * Copyright (c) 1993 The Regents of the University of California.
 * 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 the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 *	from: Steve McCanne's microtime code
 *	$Id: microtime.s,v 1.5 1996/10/09 19:47:38 bde Exp $
 */

#include "opt_cpu.h"

#include <machine/asmacros.h>

#include <i386/isa/icu.h>
#ifdef PC98
#include <pc98/pc98/pc98.h>
#else
#include <i386/isa/isa.h>
#endif
#include <i386/isa/timerreg.h>

ENTRY(microtime)

#if defined(I586_CPU) || defined(I686_CPU)
	movl	_i586_ctr_freq, %ecx
	testl	%ecx, %ecx
	jne	pentium_microtime
#else
	xorl	%ecx, %ecx	/* clear ecx */
#endif

	movb	$TIMER_SEL0|TIMER_LATCH, %al	/* prepare to latch */

	pushfl
	cli			/* disable interrupts */

	outb	%al, $TIMER_MODE	/* latch timer 0's counter */
	inb	$TIMER_CNTR0, %al	/* read counter value, LSB first */
	movb	%al, %cl
	inb	$TIMER_CNTR0, %al
	movb	%al, %ch

	/*
	 * Now check for counter overflow.  This is tricky because the
	 * timer chip doesn't let us atomically read the current counter
	 * value and the output state (i.e., overflow state).  We have
	 * to read the ICU interrupt request register (IRR) to see if the
	 * overflow has occured.  Because we lack atomicity, we use
	 * the (very accurate) heuristic that we only check for
	 * overflow if the value read is close to the interrupt period.
	 * E.g., if we just checked the IRR, we might read a non-overflowing
	 * value close to 0, experience overflow, then read this overflow
	 * from the IRR, and mistakenly add a correction to the "close
	 * to zero" value.
	 *
	 * We compare the counter value to the prepared overflow threshold.
	 * If the counter value is less than this, we assume the counter
	 * didn't overflow between disabling timer interrupts and latching
	 * the counter value above.  For example, we assume that interrupts
	 * are enabled when we are called (or were disabled just a few
	 * cycles before we are called and that the instructions before the
	 * "cli" are fast) and that the "cli" and "outb" instructions take
	 * less than 10 timer cycles to execute.  The last assumption is
	 * very safe.
	 *
	 * Otherwise, the counter might have overflowed.  We check for this
	 * condition by reading the interrupt request register out of the ICU.
	 * If it overflowed, we add in one clock period.
	 *
	 * The heuristic is "very accurate" because it works 100% if we're
	 * called with interrupts enabled.  Otherwise, it might not work.
	 * Currently, only siointrts() calls us with interrupts disabled, so
	 * the problem can be avoided at some cost to the general case.  The
	 * costs are complications in callers to disable interrupts in
	 * IO_ICU1 and extra reads of the IRR forced by a conservative
	 * overflow threshold.
	 *
	 * In 2.0, we are called at splhigh() from mi_switch(), so we have
	 * to allow for the overflow bit being in ipending instead of in
	 * the IRR.  Our caller may have executed many instructions since
	 * ipending was set, so the heuristic for the IRR is inappropriate
	 * for ipending.  However, we don't need another heuristic, since
	 * the "cli" suffices to lock ipending.
	 */

	movl	_timer0_max_count, %edx	/* prepare for 2 uses */

	testb	$IRQ0, _ipending	/* is a soft timer interrupt pending? */
	jne	overflow

	/* Do we have a possible overflow condition? */
	cmpl	_timer0_overflow_threshold, %ecx
	jbe	1f

	inb	$IO_ICU1, %al	/* read IRR in ICU */
	testb	$IRQ0, %al	/* is a hard timer interrupt pending? */
	je	1f
overflow:
	subl	%edx, %ecx	/* some intr pending, count timer down through 0 */
1:

	/*
	 * Subtract counter value from max count since it is a count-down value.
	 */
	subl	%ecx, %edx

	/* Adjust for partial ticks. */
	addl	_timer0_prescaler_count, %edx

	/*
	 * To divide by 1.193200, we multiply by 27465 and shift right by 15.
	 *
	 * The multiplier was originally calculated to be
	 *
	 *	2^18 * 1000000 / 1193200 = 219698.
	 *
	 * The frequency is 1193200 to be compatible with rounding errors in
	 * the calculation of the usual maximum count.  2^18 is the largest
	 * power of 2 such that multiplying `i' by it doesn't overflow for i
	 * in the range of interest ([0, 11932 + 5)).  We adjusted the
	 * multiplier a little to minimise the average of
	 *
	 *	fabs(i / 1.1193200 - ((multiplier * i) >> 18))
	 *
	 * for i in the range and then removed powers of 2 to speed up the
	 * multiplication and to avoid overflow for i outside the range
	 * (i may be as high as 2^17 if the timer is programmed to its
	 * maximum maximum count).  The absolute error is less than 1 for
	 * all i in the range.
	 */

#ifdef	PC98
#ifndef AUTO_CLOCK
#ifndef	PC98_8M
#if 0
	imul	$6667, %edx
#else
	leal	(%edx,%edx,4), %eax	/* a = 5 */
	leal	(%edx,%eax,2), %eax	/* a = 11 */
	movl	%eax, %ecx		/* c = 11 */
	addl	%edx, %eax		/* a = 12 */
	addl	%edx, %eax		/* a = 13 */
	shl	$9, %eax		/* a = 6656 */
	addl	%ecx, %eax		/* a = 6667 */
#endif /* 0 */
	shr	$14, %eax
#else	/* !PC98_8M */
#if 0
	imul	$16411, %edx
#else
	leal	(%edx,%edx,2), %eax	/* a = 3 */
	leal	(%eax,%eax,8), %eax	/* a = 27 */
	movl	%eax, %ecx		/* c = 27 */
	movl	%edx, %eax		/* a = 1 */
	shl	$14, %eax		/* a = 16384 */
	addl	%ecx, %eax		/* a = 16411 */
#endif /* 0 */
	shr	$15, %eax
#endif	/* !PC98_8M */
#else /* !AUTO_CLOCK */
	.globl	_pc98_system_parameter
	testb	$0x80, _pc98_system_parameter + 0x501 - 0x400
	jnz	1f
#if 0
	imul	$6667, %edx
#else
	leal	(%edx,%edx,4), %eax	/* a = 5 */
	leal	(%edx,%eax,2), %eax	/* a = 11 */
	movl	%eax, %ecx		/* c = 11 */
	addl	%edx, %eax		/* a = 12 */
	addl	%edx, %eax		/* a = 13 */
	shl	$9, %eax		/* a = 6656 */
	addl	%ecx, %eax		/* a = 6667 */
#endif /* 0 */
	shr	$14, %eax
	jmp	2f
1:
#if 0
	imul	$16411, %edx
#else
	leal	(%edx,%edx,2), %eax	/* a = 3 */
	leal	(%eax,%eax,8), %eax	/* a = 27 */
	movl	%eax, %ecx		/* c = 27 */
	movl	%edx, %eax		/* a = 1 */
	shl	$14, %eax		/* a = 16384 */
	addl	%ecx, %eax		/* a = 16411 */
#endif /* 0 */
	shr	$15, %eax
2:
#endif	/* !AUTO_CLOCK */
#else	/* IBM-PC */
#if 0
	imul	$27465, %edx				/* 25 cycles on a 486 */
#else
	leal	(%edx,%edx,2), %eax	/* a = 3	2 cycles on a 486   */
	leal	(%edx,%eax,4), %eax	/* a = 13	2		    */
	movl	%eax, %ecx		/* c = 13	1		    */
	shl	$5, %eax		/* a = 416	2		    */
	addl	%ecx, %eax		/* a = 429	1		    */
	leal	(%edx,%eax,8), %eax	/* a = 3433	2		    */
	leal	(%edx,%eax,8), %eax	/* a = 27465	2 (total 12 cycles) */
#endif /* 0 */
	shr	$15, %eax
#endif /* PC98 */

common_microtime:
	addl	_time+4, %eax	/* usec += time.tv_sec */
	movl	_time, %edx	/* sec = time.tv_sec */

	popfl			/* restore interrupt mask */

	cmpl	$1000000, %eax	/* usec valid? */
	jb	1f
	subl	$1000000, %eax	/* adjust usec */
	incl	%edx		/* bump sec */
1:
	movl	4(%esp), %ecx	/* load timeval pointer arg */
	movl	%edx, (%ecx)	/* tvp->tv_sec = sec */
	movl	%eax, 4(%ecx)	/* tvp->tv_usec = usec */

	ret

#if defined(I586_CPU) || defined(I686_CPU)
	ALIGN_TEXT
pentium_microtime:
	pushfl
	cli
	.byte	0x0f, 0x31	/* RDTSC */
	subl	_i586_ctr_bias, %eax
	mull	_i586_ctr_multiplier
	movl	%edx, %eax
	jmp	common_microtime
#endif
