1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Time operations for IP22 machines. Original code may come from 7 * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure) 8 * 9 * Copyright (C) 2001 by Ladislav Michl 10 */ 11#include <linux/kernel.h> 12#include <linux/interrupt.h> 13#include <linux/kernel_stat.h> 14#include <linux/time.h> 15 16#include <asm/cpu.h> 17#include <asm/mipsregs.h> 18#include <asm/io.h> 19#include <asm/irq.h> 20#include <asm/ds1286.h> 21#include <asm/sgialib.h> 22#include <asm/sgi/sgint23.h> 23#include <asm/sgi/sgigio.h> 24#include <asm/time.h> 25 26/* 27 * note that mktime uses month from 1 to 12 while to_tm 28 * uses 0 to 11. 29 */ 30static unsigned long indy_rtc_get_time(void) 31{ 32 unsigned char yrs, mon, day, hrs, min, sec; 33 unsigned char save_control; 34 35 save_control = CMOS_READ(RTC_CMD); 36 CMOS_WRITE((save_control|RTC_TE), RTC_CMD); 37 38 sec = CMOS_READ(RTC_SECONDS); 39 min = CMOS_READ(RTC_MINUTES); 40 hrs = CMOS_READ(RTC_HOURS) & 0x1f; 41 day = CMOS_READ(RTC_DATE); 42 mon = CMOS_READ(RTC_MONTH) & 0x1f; 43 yrs = CMOS_READ(RTC_YEAR); 44 45 CMOS_WRITE(save_control, RTC_CMD); 46 47 BCD_TO_BIN(sec); 48 BCD_TO_BIN(min); 49 BCD_TO_BIN(hrs); 50 BCD_TO_BIN(day); 51 BCD_TO_BIN(mon); 52 BCD_TO_BIN(yrs); 53 54 if (yrs < 45) 55 yrs += 30; 56 if ((yrs += 40) < 70) 57 yrs += 100; 58 59 return mktime((int)yrs + 1900, mon, day, hrs, min, sec); 60} 61 62static int indy_rtc_set_time(unsigned long tim) 63{ 64 struct rtc_time tm; 65 unsigned char save_control; 66 67 to_tm(tim, &tm); 68 69 tm.tm_mon += 1; /* tm_mon starts at zero */ 70 tm.tm_year -= 1940; 71 if (tm.tm_year >= 100) 72 tm.tm_year -= 100; 73 74 BIN_TO_BCD(tm.tm_sec); 75 BIN_TO_BCD(tm.tm_min); 76 BIN_TO_BCD(tm.tm_hour); 77 BIN_TO_BCD(tm.tm_mday); 78 BIN_TO_BCD(tm.tm_mon); 79 BIN_TO_BCD(tm.tm_year); 80 81 save_control = CMOS_READ(RTC_CMD); 82 CMOS_WRITE((save_control|RTC_TE), RTC_CMD); 83 84 CMOS_WRITE(tm.tm_year, RTC_YEAR); 85 CMOS_WRITE(tm.tm_mon, RTC_MONTH); 86 CMOS_WRITE(tm.tm_mday, RTC_DATE); 87 CMOS_WRITE(tm.tm_hour, RTC_HOURS); 88 CMOS_WRITE(tm.tm_min, RTC_MINUTES); 89 CMOS_WRITE(tm.tm_sec, RTC_SECONDS); 90 CMOS_WRITE(0, RTC_HUNDREDTH_SECOND); 91 92 CMOS_WRITE(save_control, RTC_CMD); 93 94 return 0; 95} 96 97static unsigned long dosample(volatile unsigned char *tcwp, 98 volatile unsigned char *tc2p) 99{ 100 u32 ct0, ct1; 101 volatile u8 msb, lsb; 102 103 /* Start the counter. */ 104 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MRGEN); 105 *tc2p = (SGINT_TCSAMP_COUNTER & 0xff); 106 *tc2p = (SGINT_TCSAMP_COUNTER >> 8); 107 108 /* Get initial counter invariant */ 109 ct0 = read_c0_count(); 110 111 /* Latch and spin until top byte of counter2 is zero */ 112 do { 113 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT); 114 lsb = *tc2p; 115 msb = *tc2p; 116 ct1 = read_c0_count(); 117 } while(msb); 118 119 /* Stop the counter. */ 120 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST); 121 122 /* 123 * Return the difference, this is how far the r4k counter increments 124 * for every 1/HZ seconds. We round off the nearest 1 MHz of master 125 * clock (= 1000000 / 100 / 2 = 5000 count). 126 */ 127 return ((ct1 - ct0) / 5000) * 5000; 128} 129 130/* 131 * Here we need to calibrate the cycle counter to at least be close. 132 */ 133void indy_time_init(void) 134{ 135 struct sgi_ioc_timers *p; 136 volatile unsigned char *tcwp, *tc2p; 137 unsigned long r4k_ticks[3]; 138 unsigned long r4k_tick; 139 140 /* Figure out the r4k offset, the algorithm is very simple 141 * and works in _all_ cases as long as the 8254 counter 142 * register itself works ok (as an interrupt driving timer 143 * it does not because of bug, this is why we are using 144 * the onchip r4k counter/compare register to serve this 145 * purpose, but for r4k_offset calculation it will work 146 * ok for us). There are other very complicated ways 147 * of performing this calculation but this one works just 148 * fine so I am not going to futz around. ;-) 149 */ 150 p = ioc_timers; 151 tcwp = &p->tcword; 152 tc2p = &p->tcnt2; 153 154 printk(KERN_INFO "Calibrating system timer... "); 155 dosample(tcwp, tc2p); /* Prime cache. */ 156 dosample(tcwp, tc2p); /* Prime cache. */ 157 /* Zero is NOT an option. */ 158 do { 159 r4k_ticks[0] = dosample (tcwp, tc2p); 160 } while (!r4k_ticks[0]); 161 do { 162 r4k_ticks[1] = dosample (tcwp, tc2p); 163 } while (!r4k_ticks[1]); 164 165 if (r4k_ticks[0] != r4k_ticks[1]) { 166 printk ("warning: timer counts differ, retrying..."); 167 r4k_ticks[2] = dosample (tcwp, tc2p); 168 if (r4k_ticks[2] == r4k_ticks[0] 169 || r4k_ticks[2] == r4k_ticks[1]) 170 r4k_tick = r4k_ticks[2]; 171 else { 172 printk ("disagreement, using average..."); 173 r4k_tick = (r4k_ticks[0] + r4k_ticks[1] 174 + r4k_ticks[2]) / 3; 175 } 176 } else 177 r4k_tick = r4k_ticks[0]; 178 179 printk("%d [%d.%02d MHz CPU]\n", (int) r4k_tick, 180 (int) (r4k_tick / 5000), (int) (r4k_tick % 5000) / 50); 181 182 mips_counter_frequency = r4k_tick * HZ; 183} 184 185/* Generic SGI handler for (spurious) 8254 interrupts */ 186void indy_8254timer_irq(struct pt_regs *regs) 187{ 188 int cpu = smp_processor_id(); 189 int irq = SGI_8254_0_IRQ; 190 ULONG cnt; 191 char c; 192 193 irq_enter(cpu, irq); 194 kstat.irqs[cpu][irq]++; 195 printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); 196 ArcRead(0, &c, 1, &cnt); 197 ArcEnterInteractiveMode(); 198 irq_exit(cpu, irq); 199} 200 201void indy_r4k_timer_interrupt(struct pt_regs *regs) 202{ 203 int cpu = smp_processor_id(); 204 int irq = SGI_TIMER_IRQ; 205 206 irq_enter(cpu, irq); 207 kstat.irqs[cpu][irq]++; 208 timer_interrupt(irq, NULL, regs); 209 irq_exit(cpu, irq); 210 211 if (softirq_pending(cpu)) 212 do_softirq(); 213} 214 215extern int setup_irq(unsigned int irq, struct irqaction *irqaction); 216 217static void indy_timer_setup(struct irqaction *irq) 218{ 219 unsigned long count; 220 221 /* over-write the handler, we use our own way */ 222 irq->handler = no_action; 223 224 /* set time for first interrupt */ 225 count = read_c0_count(); 226 count += mips_counter_frequency / HZ; 227 write_c0_compare(count); 228 229 /* setup irqaction */ 230 setup_irq(SGI_TIMER_IRQ, irq); 231} 232 233void sgitime_init(void) 234{ 235 /* setup hookup functions */ 236 rtc_get_time = indy_rtc_get_time; 237 rtc_set_time = indy_rtc_set_time; 238 239 board_time_init = indy_time_init; 240 board_timer_setup = indy_timer_setup; 241} 242