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