11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * License.  See the file "COPYING" in the main directory of this archive
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for more details.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Time operations for IP22 machines. Original code may come from
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure)
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 by Ladislav Michl
1054d0a216f40e060ba4265bb851cc36b3ca55d1a8Ralf Baechle * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org)
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bcd.h>
13334955ef964bee9d3b1e20966847eee28cfd05f6Ralf Baechle#include <linux/i8253.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
15046f8f705ba684312f82e36abbd42c080f4e4e9aRalf Baechle#include <linux/irq.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel_stat.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/time.h>
208f99a162653531ef25a3dd0f92bfb6332cd2b295Wu Zhangjin#include <linux/ftrace.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/cpu.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/mipsregs.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/time.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sgialib.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sgi/ioc.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sgi/hpc3.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sgi/ip22.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long dosample(void)
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 ct0, ct1;
356fd78fc1fa3ed1e70501c978c2d0bef94320252fRalf Baechle	u8 msb;
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start the counter. */
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 SGINT_TCWORD_MRGEN);
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8;
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get initial counter invariant */
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ct0 = read_c0_count();
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Latch and spin until top byte of counter2 is zero */
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
4878709b9df35346965b214e0e548412748d147776Ralf Baechle		writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword);
496fd78fc1fa3ed1e70501c978c2d0bef94320252fRalf Baechle		(void) readb(&sgint->tcnt2);
5078709b9df35346965b214e0e548412748d147776Ralf Baechle		msb = readb(&sgint->tcnt2);
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ct1 = read_c0_count();
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (msb);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Stop the counter. */
5501e9943c79ad4edb2c0b76c99029e34d704223ceThomas Bogendoerfer	writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST,
5601e9943c79ad4edb2c0b76c99029e34d704223ceThomas Bogendoerfer	       &sgint->tcword);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Return the difference, this is how far the r4k counter increments
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * for every 1/HZ seconds. We round off the nearest 1 MHz of master
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * clock (= 1000000 / HZ / 2).
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6278709b9df35346965b214e0e548412748d147776Ralf Baechle
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Here we need to calibrate the cycle counter to at least be close.
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
694b550488f894c899aa54dc935c8fee47bca2b7dfRalf Baechle__init void plat_time_init(void)
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long r4k_ticks[3];
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long r4k_tick;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7442a3b4f25af8f8d77feddf27f839fa0628dbff1aRalf Baechle	/*
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Figure out the r4k offset, the algorithm is very simple and works in
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * _all_ cases as long as the 8254 counter register itself works ok (as
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * an interrupt driving timer it does not because of bug, this is why
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * we are using the onchip r4k counter/compare register to serve this
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * purpose, but for r4k_offset calculation it will work ok for us).
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * There are other very complicated ways of performing this calculation
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * but this one works just fine so I am not going to futz around. ;-)
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "Calibrating system timer... ");
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dosample();	/* Prime cache. */
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dosample();	/* Prime cache. */
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Zero is NOT an option. */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r4k_ticks[0] = dosample();
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (!r4k_ticks[0]);
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r4k_ticks[1] = dosample();
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (!r4k_ticks[1]);
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r4k_ticks[0] != r4k_ticks[1]) {
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("warning: timer counts differ, retrying... ");
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r4k_ticks[2] = dosample();
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (r4k_ticks[2] == r4k_ticks[0]
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    || r4k_ticks[2] == r4k_ticks[1])
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			r4k_tick = r4k_ticks[2];
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else {
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("disagreement, using average... ");
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   + r4k_ticks[2]) / 3;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r4k_tick = r4k_ticks[0];
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick,
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(int) (r4k_tick / (500000 / HZ)),
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(int) (r4k_tick % (500000 / HZ)));
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mips_hpt_frequency = r4k_tick * HZ;
113d865bea4dace1d42995a6cf552bc4863842623f4Ralf Baechle
114d865bea4dace1d42995a6cf552bc4863842623f4Ralf Baechle	if (ip22_is_fullhouse())
115d865bea4dace1d42995a6cf552bc4863842623f4Ralf Baechle		setup_pit_timer();
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Generic SGI handler for (spurious) 8254 interrupts */
1198f99a162653531ef25a3dd0f92bfb6332cd2b295Wu Zhangjinvoid __irq_entry indy_8254timer_irq(void)
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int irq = SGI_8254_0_IRQ;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ULONG cnt;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char c;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq_enter();
126d2287f5ebea9ff2487d614719775f0b03fce15f6Mike Travis	kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ArcRead(0, &c, 1, &cnt);
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ArcEnterInteractiveMode();
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq_exit();
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
132