1/* A couple of routines to implement a low-overhead timer for drivers */
2
3 /*
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 */
9#include "grub.h"
10#include "osdep.h"
11#include "io.h"
12#include "timer.h"
13#include "latch.h"
14
15void __load_timer2(unsigned int ticks)
16{
17	/*
18	 * Now let's take care of PPC channel 2
19	 *
20	 * Set the Gate high, program PPC channel 2 for mode 0,
21	 * (interrupt on terminal count mode), binary count,
22	 * load 5 * LATCH count, (LSB and MSB) to begin countdown.
23	 *
24	 * Note some implementations have a bug where the high bits byte
25	 * of channel 2 is ignored.
26	 */
27	/* Set up the timer gate, turn off the speaker */
28	/* Set the Gate high, disable speaker */
29	outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
30	/* binary, mode 0, LSB/MSB, Ch 2 */
31	outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
32	/* LSB of ticks */
33	outb(ticks & 0xFF, TIMER2_PORT);
34	/* MSB of ticks */
35	outb(ticks >> 8, TIMER2_PORT);
36}
37
38static int __timer2_running(void)
39{
40	return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
41}
42
43#if !defined(CONFIG_TSC_CURRTICKS)
44void setup_timers(void)
45{
46	return;
47}
48
49void load_timer2(unsigned int ticks)
50{
51	return __load_timer2(ticks);
52}
53
54int timer2_running(void)
55{
56	return __timer2_running();
57}
58
59void ndelay(unsigned int nsecs)
60{
61	waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000);
62}
63void udelay(unsigned int usecs)
64{
65	waiton_timer2((usecs * TICKS_PER_MS)/1000);
66}
67#endif /* !defined(CONFIG_TSC_CURRTICKS) */
68
69#if defined(CONFIG_TSC_CURRTICKS)
70
71#define rdtsc(low,high) \
72     __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
73
74#define rdtscll(val) \
75     __asm__ __volatile__ ("rdtsc" : "=A" (val))
76
77
78/* Number of clock ticks to time with the rtc */
79#define LATCH 0xFF
80
81#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH)
82#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC)
83
84static void sleep_latch(void)
85{
86	__load_timer2(LATCH);
87	while(__timer2_running());
88}
89
90/* ------ Calibrate the TSC -------
91 * Time how long it takes to excute a loop that runs in known time.
92 * And find the convertion needed to get to CLOCK_TICK_RATE
93 */
94
95
96static unsigned long long calibrate_tsc(void)
97{
98	unsigned long startlow, starthigh;
99	unsigned long endlow, endhigh;
100
101	rdtsc(startlow,starthigh);
102	sleep_latch();
103	rdtsc(endlow,endhigh);
104
105	/* 64-bit subtract - gcc just messes up with long longs */
106	__asm__("subl %2,%0\n\t"
107		"sbbl %3,%1"
108		:"=a" (endlow), "=d" (endhigh)
109		:"g" (startlow), "g" (starthigh),
110		"0" (endlow), "1" (endhigh));
111
112	/* Error: ECPUTOOFAST */
113	if (endhigh)
114		goto bad_ctc;
115
116	endlow *= TICKS_PER_LATCH;
117	return endlow;
118
119	/*
120	 * The CTC wasn't reliable: we got a hit on the very first read,
121	 * or the CPU was so fast/slow that the quotient wouldn't fit in
122	 * 32 bits..
123	 */
124bad_ctc:
125	printf("bad_ctc\n");
126	return 0;
127}
128
129static unsigned long clocks_per_tick;
130void setup_timers(void)
131{
132	if (!clocks_per_tick) {
133		clocks_per_tick = calibrate_tsc();
134		/* Display the CPU Mhz to easily test if the calibration was bad */
135		printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
136	}
137}
138
139unsigned long currticks(void)
140{
141	unsigned long clocks_high, clocks_low;
142	unsigned long currticks;
143	/* Read the Time Stamp Counter */
144	rdtsc(clocks_low, clocks_high);
145
146	/* currticks = clocks / clocks_per_tick; */
147	__asm__("divl %1"
148		:"=a" (currticks)
149		:"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high));
150
151
152	return currticks;
153}
154
155static unsigned long long timer_timeout;
156static int __timer_running(void)
157{
158	unsigned long long now;
159	rdtscll(now);
160	return now < timer_timeout;
161}
162
163void udelay(unsigned int usecs)
164{
165	unsigned long long now;
166	rdtscll(now);
167	timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
168	while(__timer_running());
169}
170void ndelay(unsigned int nsecs)
171{
172	unsigned long long now;
173	rdtscll(now);
174	timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
175	while(__timer_running());
176}
177
178void load_timer2(unsigned int timer2_ticks)
179{
180	unsigned long long now;
181	unsigned long clocks;
182	rdtscll(now);
183	clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
184	timer_timeout = now + clocks;
185}
186
187int timer2_running(void)
188{
189	return __timer_running();
190}
191
192#endif /* RTC_CURRTICKS */
193