mct.c revision 1.7 1 /* $NetBSD: mct.c,v 1.7 2015/12/21 00:54:35 marty Exp $ */
2
3 /*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Reinoud Zandijk.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33
34 __KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.7 2015/12/21 00:54:35 marty Exp $");
35
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/device.h>
39 #include <sys/intr.h>
40 #include <sys/kernel.h>
41 #include <sys/proc.h>
42 #include <sys/systm.h>
43 #include <sys/timetc.h>
44
45 #include <prop/proplib.h>
46
47 #include <arm/samsung/exynos_reg.h>
48 #include <arm/samsung/exynos_var.h>
49 #include <arm/samsung/mct_reg.h>
50 #include <arm/samsung/mct_var.h>
51
52 #include <dev/fdt/fdtvar.h>
53
54 static int mct_match(device_t, cfdata_t, void *);
55 static void mct_attach(device_t, device_t, void *);
56
57 static int clockhandler(void *);
58
59 CFATTACH_DECL_NEW(exyo_mct, 0, mct_match, mct_attach, NULL, NULL);
60
61
62 #if 0
63 static struct timecounter mct_timecounter = {
64 .tc_get_timecount = mct_get_timecount,
65 .tc_poll_pps = 0,
66 .tc_counter_mask = ~0u,
67 .tc_frequency = 0, /* set by cpu_initclocks() */
68 .tc_name = NULL, /* set by cpu_initclocks() */
69 .tc_quality = 500, /* why 500? */
70 .tc_priv = &mct_sc,
71 .tc_next = NULL,
72 };
73 #endif
74
75
76 static inline uint32_t
77 mct_read_global(struct mct_softc *sc, bus_size_t o)
78 {
79 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o);
80 }
81
82
83 static inline void
84 mct_write_global(struct mct_softc *sc, bus_size_t o, uint32_t v)
85 {
86 bus_size_t wreg;
87 uint32_t bit;
88 int i;
89
90 /* do the write */
91 bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v);
92 // printf("%s: write %#x at %#x\n",
93 // __func__, ((uint32_t) sc->sc_bsh + (uint32_t) o), v);
94
95 /* dependent on the write address, do the ack dance */
96 if (o == MCT_G_CNT_L || o == MCT_G_CNT_U) {
97 wreg = MCT_G_CNT_WSTAT;
98 bit = (o == MCT_G_CNT_L) ? G_CNT_WSTAT_L : G_CNT_WSTAT_U;
99 } else {
100 wreg = MCT_G_WSTAT;
101 switch (o) {
102 case MCT_G_COMP0_L:
103 bit = G_WSTAT_COMP0_L;
104 break;
105 case MCT_G_COMP0_U:
106 bit = G_WSTAT_COMP0_U;
107 break;
108 case MCT_G_COMP0_ADD_INCR:
109 bit = G_WSTAT_ADD_INCR;
110 break;
111 case MCT_G_TCON:
112 bit = G_WSTAT_TCON;
113 break;
114 default:
115 /* all other registers */
116 return;
117 }
118 }
119
120 /* wait for ack */
121 for (i = 0; i < 10000000; i++) {
122 /* value accepted by the hardware/hal ? */
123 if (mct_read_global(sc, wreg) & bit) {
124 /* ack */
125 bus_space_write_4(sc->sc_bst, sc->sc_bsh, wreg, bit);
126 return;
127 }
128 }
129 panic("MCT hangs after writing %#x at %#x", v, (uint32_t) o);
130 }
131
132
133 static int
134 mct_match(device_t parent, cfdata_t cf, void *aux)
135 {
136 const char * const compatible[] = { "samsung,exynos4210-mct",
137 NULL };
138
139 struct fdt_attach_args * const faa = aux;
140 return of_match_compatible(faa->faa_phandle, compatible);
141 }
142
143
144 static void
145 mct_attach(device_t parent, device_t self, void *aux)
146 {
147 struct mct_softc * const sc = &mct_sc;
148 // prop_dictionary_t dict = device_properties(self);
149 // char freqbuf[sizeof("XXX SHz")];
150 struct fdt_attach_args * const faa = aux;
151 bus_addr_t addr;
152 bus_size_t size;
153 int error;
154
155 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
156 aprint_error(": couldn't get registers\n");
157 return;
158 }
159
160 self->dv_private = sc;
161 sc->sc_dev = self;
162 sc->sc_bst = faa->faa_bst;
163 /* MJF: Need to get irq from the dtd */
164 // sc->sc_irq = exyo->exyo_loc.loc_intr;
165
166 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
167 if (error) {
168 aprint_error(": couldn't map %#llx: %d",
169 (uint64_t)addr, error);
170 return;
171 }
172
173 aprint_naive("\n");
174 aprint_normal(": Exynos SoC multi core timer (64 bits)\n");
175
176 evcnt_attach_dynamic(&sc->sc_ev_missing_ticks, EVCNT_TYPE_MISC, NULL,
177 device_xname(self), "missing interrupts");
178
179 sc->sc_global_ih = intr_establish(sc->sc_irq, IPL_CLOCK, IST_EDGE,
180 clockhandler, NULL);
181 if (sc->sc_global_ih == NULL)
182 panic("%s: unable to register timer interrupt", __func__);
183 aprint_normal_dev(sc->sc_dev, "interrupting on irq %d\n", sc->sc_irq);
184 }
185
186
187 static inline uint64_t
188 mct_gettime(struct mct_softc *sc)
189 {
190 uint32_t lo, hi;
191 do {
192 hi = mct_read_global(sc, MCT_G_CNT_U);
193 lo = mct_read_global(sc, MCT_G_CNT_L);
194 } while (hi != mct_read_global(sc, MCT_G_CNT_U));
195 return ((uint64_t) hi << 32) | lo;
196 }
197
198
199 /* interrupt handler */
200 static int
201 clockhandler(void *arg)
202 {
203 struct clockframe * const cf = arg;
204 struct mct_softc * const sc = &mct_sc;
205 const uint64_t now = mct_gettime(sc);
206 int64_t delta = now - sc->sc_lastintr;
207 int64_t periods = delta / sc->sc_autoinc;
208
209 KASSERT(delta >= 0);
210 KASSERT(periods >= 0);
211
212 /* ack the interrupt */
213 mct_write_global(sc, MCT_G_INT_CSTAT, G_INT_CSTAT_CLEAR);
214
215 /* check if we missed clock interrupts */
216 if (periods > 1)
217 sc->sc_ev_missing_ticks.ev_count += periods - 1;
218
219 sc->sc_lastintr = now;
220 hardclock(cf);
221
222 /* handled */
223 return 1;
224 }
225
226
227 void
228 mct_init_cpu_clock(struct cpu_info *ci)
229 {
230 struct mct_softc * const sc = &mct_sc;
231 uint64_t now = mct_gettime(sc);
232 uint64_t then;
233 uint32_t tcon;
234
235 KASSERT(ci == curcpu());
236
237 sc->sc_lastintr = now;
238
239 /* get current config */
240 tcon = mct_read_global(sc, MCT_G_TCON);
241
242 /* setup auto increment */
243 mct_write_global(sc, MCT_G_COMP0_ADD_INCR, sc->sc_autoinc);
244
245 /* (re)setup comparator */
246 then = now + sc->sc_autoinc;
247 mct_write_global(sc, MCT_G_COMP0_L, (uint32_t) then);
248 mct_write_global(sc, MCT_G_COMP0_U, (uint32_t) (then >> 32));
249 tcon |= G_TCON_COMP0_AUTOINC;
250 tcon |= G_TCON_COMP0_ENABLE;
251
252 /* start timer */
253 tcon |= G_TCON_START;
254
255 /* enable interrupt */
256 mct_write_global(sc, MCT_G_INT_ENB, G_INT_ENB_ENABLE);
257
258 /* update config, starting the thing */
259 mct_write_global(sc, MCT_G_TCON, tcon);
260 }
261
262
263 #if 0
264 void
265 cpu_initclocks(void)
266 {
267 struct mct_softc * const sc = &mct_sc;
268
269 sc->sc_autoinc = sc->sc_freq / hz;
270 mct_init_cpu_clock(curcpu());
271
272 mct_timecounter.tc_name = device_xname(sc->sc_dev);
273 mct_timecounter.tc_frequency = sc->sc_freq;
274
275 tc_init(&mct_timecounter);
276
277 #if 0
278 {
279 uint64_t then, now;
280
281 printf("testing timer\n");
282 for (int i = 0; i < 200; i++) {
283 printf("cstat %d\n", mct_read_global(sc, MCT_G_INT_CSTAT));
284 then = mct_get_timecount(&mct_timecounter);
285 do {
286 now = mct_get_timecount(&mct_timecounter);
287 } while (now == then);
288 printf("\tgot %"PRIu64"\n", now);
289 for (int j = 0; j < 90000; j++);
290 }
291 printf("passed\n");
292 }
293 #endif
294 }
295
296 #endif
297