summaryrefslogtreecommitdiff
path: root/sys/src/9/teg2/clock-tegra.c
blob: a90e9b973ff16bc9723445474fb8102ac83ea34b (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
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * tegra 2 SoC clocks; excludes cortex-a timers.
 *
 * SoC provides these shared clocks:
 * 4 29-bit count-down `timers' @ 1MHz,
 * 1 32-bit count-up time-stamp counter @ 1MHz,
 * and a real-time clock @ 32KHz.
 * the tegra watchdog (tegra 2 ref man §5.4.1) is tied to timers, not rtc.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "arm.h"

typedef struct Shrdtmr Shrdtmr;
typedef struct µscnt µscnt;

/* tegra2 shared-intr timer registers */
struct Shrdtmr {		/* 29-bit count-down timer (4); unused */
	ulong	trigger;
	ulong	prescnt;
};

enum {
	/* trigger bits */
	Enable =	1u<<31,
	Periodintr =	1<<30,
	Countmask =	MASK(29),

	/* prescnt bits */
	Intrclr =	1<<30,
	/* Countmask is ro */
};

struct µscnt {		/* tegra2 shared 32-bit count-up µs counter (1) */
	ulong	cntr;
	/*
	 * oscillator clock fraction - 1; initially 0xb (11) from u-boot
	 * for 12MHz periphclk.
	 */
	ulong	cfg;
	uchar	_pad0[0x3c - 0x8];
	ulong	freeze;
};

enum {
	/* cfg bits */
	Dividendshift =	8,
	Dividendmask =	MASK(8),
	Divisorshift =	0,
	Divisormask =	MASK(8),
};

void
tegclockintr(void)
{
	int junk;
	Shrdtmr *tmr;

	/* appease the tegra dog */
	tmr = (Shrdtmr *)soc.tmr[0];
	junk = tmr->trigger;
	USED(junk);
}

/*
 * if on cpu0, shutdown the shared tegra2 watchdog timer.
 */
void
tegclockshutdown(void)
{
	Shrdtmr *tmr;

	if (m->machno == 0) {
		tmr = (Shrdtmr *)soc.tmr[0];
		tmr->prescnt = tmr->trigger = 0;
		coherence();
	}
}

void
tegwdogintr(Ureg *, void *v)
{
	int junk;
	Shrdtmr *tmr;

	tmr = (Shrdtmr *)v;
	tmr->prescnt |= Intrclr;
	coherence();
	/* the lousy documentation says we also have to read trigger */
	junk = tmr->trigger;
	USED(junk);
}

/* start tegra2 shared watch dog */
void
tegclock0init(void)
{
	Shrdtmr *tmr;

	tmr = (Shrdtmr *)soc.tmr[0];
	irqenable(Tn0irq, tegwdogintr, tmr, "tegra watchdog");

	/*
	 * tegra watchdog only fires on the second missed interrupt, thus /2.
	 */
	tmr->trigger = (Dogsectimeout * Mhz / 2 - 1) | Periodintr | Enable;
	coherence();
}

/*
 * µscnt is a freerunning timer (cycle counter); it needs no
 * initialisation, wraps and does not dispatch interrupts.
 */
void
tegclockinit(void)
{
	ulong old;
	µscnt *µs = (µscnt *)soc.µs;

	/* verify µs counter sanity */
	assert(µs->cfg == 0xb);			/* set by u-boot */
	old = µs->cntr;
	delay(1);
	assert(old != µs->cntr);
}

ulong
perfticks(void)			/* MHz rate, assumed by timing loops */
{
	ulong v;

	/* keep it non-zero to prevent m->fastclock ever going to zero. */
	v = ((µscnt *)soc.µs)->cntr;
	return v == 0? 1: v;
}