xref: /plan9/sys/src/9/teg2/clock-tegra.c (revision 3de6a9c0b3d5cf34fc4090d0bf1930d83799a7fd)
1 /*
2  * tegra 2 SoC clocks; excludes cortex-a timers.
3  *
4  * SoC provides these shared clocks:
5  * 4 29-bit count-down `timers' @ 1MHz,
6  * 1 32-bit count-up time-stamp counter @ 1MHz,
7  * and a real-time clock @ 32KHz.
8  * the tegra watchdog (tegra 2 ref man §5.4.1) is tied to timers, not rtc.
9  */
10 #include "u.h"
11 #include "../port/lib.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "arm.h"
16 
17 typedef struct Shrdtmr Shrdtmr;
18 typedef struct µscnt µscnt;
19 
20 /* tegra2 shared-intr timer registers */
21 struct Shrdtmr {		/* 29-bit count-down timer (4); unused */
22 	ulong	trigger;
23 	ulong	prescnt;
24 };
25 
26 enum {
27 	/* trigger bits */
28 	Enable =	1u<<31,
29 	Periodintr =	1<<30,
30 	Countmask =	MASK(29),
31 
32 	/* prescnt bits */
33 	Intrclr =	1<<30,
34 	/* Countmask is ro */
35 };
36 
37 struct µscnt {		/* tegra2 shared 32-bit count-up µs counter (1) */
38 	ulong	cntr;
39 	/*
40 	 * oscillator clock fraction - 1; initially 0xb (11) from u-boot
41 	 * for 12MHz periphclk.
42 	 */
43 	ulong	cfg;
44 	uchar	_pad0[0x3c - 0x8];
45 	ulong	freeze;
46 };
47 
48 enum {
49 	/* cfg bits */
50 	Dividendshift =	8,
51 	Dividendmask =	MASK(8),
52 	Divisorshift =	0,
53 	Divisormask =	MASK(8),
54 };
55 
56 void
tegclockintr(void)57 tegclockintr(void)
58 {
59 	int junk;
60 	Shrdtmr *tmr;
61 
62 	/* appease the tegra dog */
63 	tmr = (Shrdtmr *)soc.tmr[0];
64 	junk = tmr->trigger;
65 	USED(junk);
66 }
67 
68 /*
69  * if on cpu0, shutdown the shared tegra2 watchdog timer.
70  */
71 void
tegclockshutdown(void)72 tegclockshutdown(void)
73 {
74 	Shrdtmr *tmr;
75 
76 	if (m->machno == 0) {
77 		tmr = (Shrdtmr *)soc.tmr[0];
78 		tmr->prescnt = tmr->trigger = 0;
79 		coherence();
80 	}
81 }
82 
83 void
tegwdogintr(Ureg *,void * v)84 tegwdogintr(Ureg *, void *v)
85 {
86 	int junk;
87 	Shrdtmr *tmr;
88 
89 	tmr = (Shrdtmr *)v;
90 	tmr->prescnt |= Intrclr;
91 	coherence();
92 	/* the lousy documentation says we also have to read trigger */
93 	junk = tmr->trigger;
94 	USED(junk);
95 }
96 
97 /* start tegra2 shared watch dog */
98 void
tegclock0init(void)99 tegclock0init(void)
100 {
101 	Shrdtmr *tmr;
102 
103 	tmr = (Shrdtmr *)soc.tmr[0];
104 	irqenable(Tn0irq, tegwdogintr, tmr, "tegra watchdog");
105 
106 	/*
107 	 * tegra watchdog only fires on the second missed interrupt, thus /2.
108 	 */
109 	tmr->trigger = (Dogsectimeout * Mhz / 2 - 1) | Periodintr | Enable;
110 	coherence();
111 }
112 
113 /*
114  * µscnt is a freerunning timer (cycle counter); it needs no
115  * initialisation, wraps and does not dispatch interrupts.
116  */
117 void
tegclockinit(void)118 tegclockinit(void)
119 {
120 	ulong old;
121 	µscnts = (µscnt *)socs;
122 
123 	/* verify µs counter sanity */
124 	asserts->cfg == 0xb);			/* set by u-boot */
125 	old = µs->cntr;
126 	delay(1);
127 	assert(old != µs->cntr);
128 }
129 
130 ulong
perfticks(void)131 perfticks(void)			/* MHz rate, assumed by timing loops */
132 {
133 	ulong v;
134 
135 	/* keep it non-zero to prevent m->fastclock ever going to zero. */
136 	v = ((µscnt *)socs)->cntr;
137 	return v == 0? 1: v;
138 }
139