1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
#include "boot.h"
/*
* Control Word Read/Write Counter (mode 0) LSB, MSB
*/
#define PIT_RW_COUNTER0 0x30
#define PIT_RW_COUNTER1 0x70
#define PIT_RW_COUNTER2 0xB0
#define PIT_COUNTERLATCH0 0x00
#define PIT_COUNTERLATCH1 0x40
#define PIT_COUNTERLATCH2 0x80
#define PIT_MODE_0 0 /* Interrupt on Terminal Count */
#define PIT_MODE_1 2 /* Hardware Retriggeable One-shot */
#define PIT_MODE_2 4 /* Rate Generator */
#define PIT_MODE_3 6 /* Square Wave Mode */
#define PIT_MODE_4 8 /* Software Triggered Mode */
#define PIT_MODE_5 10 /* Hardware Triggered Mode (Retriggeable) */
/*
* Harris 82C54 Programmable Interval Timer
* On the Puma board the PIT is memory mapped
* starting at 0xf2000000 and with each of the 8-bit
* registers addressed on a consecutive 4-byte boundary.
*/
#undef inb
#undef outb
#define inb(port) ((*(uchar *)(port))&0xff)
#define outb(port, data) (*(uchar *)(port) = (data))
enum
{
Cnt0= 0xf2000000, /* counter locations */
Cnt1= 0xf2000004, /* ... */
Cnt2= 0xf2000008, /* ... */
Ctlw= 0xf200000c, /* control word register*/
/* commands */
Latch0= 0x00, /* latch counter 0's value */
Load0= 0x30, /* load counter 0 with 2 bytes */
Latch1= 0x40, /* latch counter 1's value */
Load1= 0x70, /* load counter 1 with 2 bytes */
/* modes */
Square= 0x06, /* periodic square wave */
RateGen= 0x04, /* rate generator */
Freq= 3686400, /* Real clock frequency */
};
static int cpufreq = 233000000;
static int aalcycles = 14;
static void
clockintr(Ureg*, void*)
{
m->ticks++;
checkalarms();
}
/*
* delay for l milliseconds more or less. delayloop is set by
* clockinit() to match the actual CPU speed.
*/
void
delay(int l)
{
l *= m->delayloop;
if(l <= 0)
l = 1;
aamloop(l);
}
void
microdelay(int l)
{
l *= m->delayloop;
l /= 1000;
if(l <= 0)
l = 1;
aamloop(l);
}
void
clockinit(void)
{
int x, y; /* change in counter */
int loops, incr;
/*
* set vector for clock interrupts
*/
setvec(V_TIMER0, clockintr, 0);
/*
* set clock for 1/HZ seconds
*/
outb(Ctlw, Load0|Square);
outb(Cnt0, (Freq/HZ)); /* low byte */
outb(Cnt0, (Freq/HZ)>>8); /* high byte */
/* find biggest loop that doesn't wrap */
incr = 16000000/(aalcycles*HZ*2);
x = 2000;
for(loops = incr; loops < 64*1024; loops += incr) {
/*
* measure time for the loop
* TEXT aamloop(SB), $-4
* _aamloop:
* MOVW R0, R0
* MOVW R0, R0
* MOVW R0, R0
* SUB $1, R0
* CMP $0, R0
* BNE _aamloop
* RET
*
* the time for the loop should be independent of external
* cache and memory system since it fits in the execution
* prefetch buffer.
*
*/
outb(Ctlw, Latch0);
x = inb(Cnt0);
x |= inb(Cnt0)<<8;
aamloop(loops);
outb(Ctlw, Latch0);
y = inb(Cnt0);
y |= inb(Cnt0)<<8;
x -= y;
if(x < 0)
x += Freq/HZ;
if(x > Freq/(3*HZ))
break;
}
/*
* counter goes at twice the frequency, once per transition,
* i.e., twice per square wave
*/
x >>= 1;
/*
* figure out clock frequency and a loop multiplier for delay().
*/
cpufreq = loops*((aalcycles*Freq)/x);
m->delayloop = (cpufreq/1000)/aalcycles; /* AAMLOOPs for 1 ms */
/*
* add in possible .2% error and convert to MHz
*/
m->speed = (cpufreq + cpufreq/500)/1000000;
}
|