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