162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Time operations for IP22 machines. Original code may come from 762306a36Sopenharmony_ci * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2001 by Ladislav Michl 1062306a36Sopenharmony_ci * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org) 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/bcd.h> 1362306a36Sopenharmony_ci#include <linux/i8253.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1962306a36Sopenharmony_ci#include <linux/time.h> 2062306a36Sopenharmony_ci#include <linux/ftrace.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/cpu.h> 2362306a36Sopenharmony_ci#include <asm/mipsregs.h> 2462306a36Sopenharmony_ci#include <asm/io.h> 2562306a36Sopenharmony_ci#include <asm/irq.h> 2662306a36Sopenharmony_ci#include <asm/time.h> 2762306a36Sopenharmony_ci#include <asm/sgialib.h> 2862306a36Sopenharmony_ci#include <asm/sgi/ioc.h> 2962306a36Sopenharmony_ci#include <asm/sgi/hpc3.h> 3062306a36Sopenharmony_ci#include <asm/sgi/ip22.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic unsigned long dosample(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci u32 ct0, ct1; 3562306a36Sopenharmony_ci u8 msb; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Start the counter. */ 3862306a36Sopenharmony_ci sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | 3962306a36Sopenharmony_ci SGINT_TCWORD_MRGEN); 4062306a36Sopenharmony_ci sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff; 4162306a36Sopenharmony_ci sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* Get initial counter invariant */ 4462306a36Sopenharmony_ci ct0 = read_c0_count(); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Latch and spin until top byte of counter2 is zero */ 4762306a36Sopenharmony_ci do { 4862306a36Sopenharmony_ci writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword); 4962306a36Sopenharmony_ci (void) readb(&sgint->tcnt2); 5062306a36Sopenharmony_ci msb = readb(&sgint->tcnt2); 5162306a36Sopenharmony_ci ct1 = read_c0_count(); 5262306a36Sopenharmony_ci } while (msb); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Stop the counter. */ 5562306a36Sopenharmony_ci writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST, 5662306a36Sopenharmony_ci &sgint->tcword); 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Return the difference, this is how far the r4k counter increments 5962306a36Sopenharmony_ci * for every 1/HZ seconds. We round off the nearest 1 MHz of master 6062306a36Sopenharmony_ci * clock (= 1000000 / HZ / 2). 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return (ct1 - ct0) / (500000/HZ) * (500000/HZ); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Here we need to calibrate the cycle counter to at least be close. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci__init void plat_time_init(void) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned long r4k_ticks[3]; 7262306a36Sopenharmony_ci unsigned long r4k_tick; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * Figure out the r4k offset, the algorithm is very simple and works in 7662306a36Sopenharmony_ci * _all_ cases as long as the 8254 counter register itself works ok (as 7762306a36Sopenharmony_ci * an interrupt driving timer it does not because of bug, this is why 7862306a36Sopenharmony_ci * we are using the onchip r4k counter/compare register to serve this 7962306a36Sopenharmony_ci * purpose, but for r4k_offset calculation it will work ok for us). 8062306a36Sopenharmony_ci * There are other very complicated ways of performing this calculation 8162306a36Sopenharmony_ci * but this one works just fine so I am not going to futz around. ;-) 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci printk(KERN_INFO "Calibrating system timer... "); 8462306a36Sopenharmony_ci dosample(); /* Prime cache. */ 8562306a36Sopenharmony_ci dosample(); /* Prime cache. */ 8662306a36Sopenharmony_ci /* Zero is NOT an option. */ 8762306a36Sopenharmony_ci do { 8862306a36Sopenharmony_ci r4k_ticks[0] = dosample(); 8962306a36Sopenharmony_ci } while (!r4k_ticks[0]); 9062306a36Sopenharmony_ci do { 9162306a36Sopenharmony_ci r4k_ticks[1] = dosample(); 9262306a36Sopenharmony_ci } while (!r4k_ticks[1]); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (r4k_ticks[0] != r4k_ticks[1]) { 9562306a36Sopenharmony_ci printk("warning: timer counts differ, retrying... "); 9662306a36Sopenharmony_ci r4k_ticks[2] = dosample(); 9762306a36Sopenharmony_ci if (r4k_ticks[2] == r4k_ticks[0] 9862306a36Sopenharmony_ci || r4k_ticks[2] == r4k_ticks[1]) 9962306a36Sopenharmony_ci r4k_tick = r4k_ticks[2]; 10062306a36Sopenharmony_ci else { 10162306a36Sopenharmony_ci printk("disagreement, using average... "); 10262306a36Sopenharmony_ci r4k_tick = (r4k_ticks[0] + r4k_ticks[1] 10362306a36Sopenharmony_ci + r4k_ticks[2]) / 3; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci } else 10662306a36Sopenharmony_ci r4k_tick = r4k_ticks[0]; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick, 10962306a36Sopenharmony_ci (int) (r4k_tick / (500000 / HZ)), 11062306a36Sopenharmony_ci (int) (r4k_tick % (500000 / HZ))); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci mips_hpt_frequency = r4k_tick * HZ; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (ip22_is_fullhouse()) 11562306a36Sopenharmony_ci setup_pit_timer(); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Generic SGI handler for (spurious) 8254 interrupts */ 11962306a36Sopenharmony_civoid __irq_entry indy_8254timer_irq(void) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci int irq = SGI_8254_0_IRQ; 12262306a36Sopenharmony_ci ULONG cnt; 12362306a36Sopenharmony_ci char c; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci irq_enter(); 12662306a36Sopenharmony_ci kstat_incr_irq_this_cpu(irq); 12762306a36Sopenharmony_ci printk(KERN_ALERT "Oops, got 8254 interrupt.\n"); 12862306a36Sopenharmony_ci ArcRead(0, &c, 1, &cnt); 12962306a36Sopenharmony_ci ArcEnterInteractiveMode(); 13062306a36Sopenharmony_ci irq_exit(); 13162306a36Sopenharmony_ci} 132