summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/hpet.c
blob: dba90ce44a53efd9878e5f12de95ffe9cea9b86e (plain)
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
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

/*
 * HPET timer
 *
 * The HPET timer is memory mapped which allows
 * faster access compared to the classic i8253.
 * This timer is not used to generate interrupts
 * as we use the LAPIC timer for that.
 * Its purpose is to measure the LAPIC timer
 * and TSC frequencies.
 */

enum {
	Cap	= 0x00/4,
	Period	= 0x04/4,
	Config	= 0x10/4,
	Isr	= 0x20/4,
	Ctrlo	= 0xF0/4,
	Ctrhi	= 0xF4/4,
};

static struct {
	Lock;
	u32int	*mmio;
	uvlong	last;
	uvlong	freq;
} hpet;

int
hpetprobe(uvlong pa)
{
	u32int cap, period;
	int mhz;

	if((hpet.mmio = vmap(pa, 1024)) == nil)
		return -1;
	cap = hpet.mmio[Cap];
	period = hpet.mmio[Period];
	if(period == 0 || period > 0x05F4E100)
		return -1;
	hpet.freq = 1000000000000000ULL / period;
	mhz = (hpet.freq + 500000) / 1000000;

	print("HPET: %llux %.8ux %d MHz \n", pa, cap, mhz);

	return 0;
}

static uvlong
hpetcpufreq(void)
{
	u32int x, y;
	uvlong a, b;
	int loops;

	ilock(&hpet);
	for(loops = 1000;;loops += 1000){
		cycles(&a);
		x = hpet.mmio[Ctrlo];
		aamloop(loops);
		cycles(&b);
		y = hpet.mmio[Ctrlo] - x;
		if(y >= hpet.freq/HZ || loops >= 1000000)
			break;
	}
	iunlock(&hpet);

	if(m->havetsc && b > a){
		b -= a;
		m->cyclefreq = b * hpet.freq / y;
		m->aalcycles = (b + loops-1) / loops;
		return m->cyclefreq;
	}
	return (vlong)loops*m->aalcycles * hpet.freq / y;
}

void
hpetinit(void)
{
	uvlong cpufreq;

	if(m->machno != 0){
		m->cpuhz = MACHP(0)->cpuhz;
		m->cpumhz = MACHP(0)->cpumhz;
		m->cyclefreq = MACHP(0)->cyclefreq;
		m->loopconst = MACHP(0)->loopconst;
		return;
	}

	/* start counting */
	hpet.mmio[Config] |= 1;

	/* measure loopconst for delay() and tsc frequencies */
	cpufreq = hpetcpufreq();

	m->loopconst = (cpufreq/1000)/m->aalcycles;	/* AAM+LOOP's for 1 ms */
	m->cpuhz = cpufreq;

	/* round to the nearest megahz */
	m->cpumhz = (cpufreq+500000)/1000000L;
	if(m->cpumhz == 0)
		m->cpumhz = 1;
}

uvlong
hpetread(uvlong *hz)
{
	uvlong ticks;

	if(hz != nil)
		*hz = hpet.freq;

	ilock(&hpet);
	ticks = hpet.last;
	ticks += hpet.mmio[Ctrlo] - (u32int)ticks;
	hpet.last = ticks;
	iunlock(&hpet);

	return ticks;
}