mct.c revision 1.10 1 /* $NetBSD: mct.c,v 1.10 2016/01/07 04:45:10 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.10 2016/01/07 04:45:10 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 #include <sys/kmem.h>
45
46 #include <prop/proplib.h>
47
48 #include <arm/samsung/exynos_reg.h>
49 #include <arm/samsung/exynos_var.h>
50 #include <arm/samsung/mct_reg.h>
51 #include <arm/samsung/mct_var.h>
52
53 #include <dev/fdt/fdtvar.h>
54
55 static int mct_match(device_t, cfdata_t, void *);
56 static void mct_attach(device_t, device_t, void *);
57
58 static int clockhandler(void *);
59
60 CFATTACH_DECL_NEW(exyo_mct, 0, mct_match, mct_attach, NULL, NULL);
61
62
63 #if 0
64 static struct timecounter mct_timecounter = {
65 .tc_get_timecount = mct_get_timecount,
66 .tc_poll_pps = 0,
67 .tc_counter_mask = ~0u,
68 .tc_frequency = 0, /* set by cpu_initclocks() */
69 .tc_name = NULL, /* set by cpu_initclocks() */
70 .tc_quality = 500, /* why 500? */
71 .tc_priv = &mct_sc,
72 .tc_next = NULL,
73 };
74 #endif
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 static int
133 mct_match(device_t parent, cfdata_t cf, void *aux)
134 {
135 const char * const compatible[] = { "samsung,exynos4210-mct",
136 NULL };
137
138 struct fdt_attach_args * const faa = aux;
139 return of_match_compatible(faa->faa_phandle, compatible);
140 }
141
142
143 static void
144 mct_attach(device_t parent, device_t self, void *aux)
145 {
146 struct mct_softc * const sc = &mct_sc;
147 struct fdt_attach_args * const faa = aux;
148 bus_addr_t addr;
149 bus_size_t size;
150 int error;
151
152 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
153 aprint_error(": couldn't get registers\n");
154 return;
155 }
156
157 self->dv_private = sc;
158 sc->sc_dev = self;
159 sc->sc_bst = faa->faa_bst;
160
161 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
162 if (error) {
163 aprint_error(": couldn't map %#llx: %d",
164 (uint64_t)addr, error);
165 return;
166 }
167
168 aprint_naive("\n");
169 aprint_normal(": Exynos SoC multi core timer (64 bits) - NOT IMPLEMENTED\n");
170
171 evcnt_attach_dynamic(&sc->sc_ev_missing_ticks, EVCNT_TYPE_MISC, NULL,
172 device_xname(self), "missing interrupts");
173
174 for (int i = 0; i < 12; i++)
175 fdtbus_intr_establish(faa->faa_phandle, i, 0, 0,
176 clockhandler, 0);
177 }
178
179 static inline uint64_t
180 mct_gettime(struct mct_softc *sc)
181 {
182 uint32_t lo, hi;
183 do {
184 hi = mct_read_global(sc, MCT_G_CNT_U);
185 lo = mct_read_global(sc, MCT_G_CNT_L);
186 } while (hi != mct_read_global(sc, MCT_G_CNT_U));
187 return ((uint64_t) hi << 32) | lo;
188 }
189
190
191 /* interrupt handler */
192 static int
193 clockhandler(void *arg)
194 {
195 struct clockframe * const cf = arg;
196 struct mct_softc * const sc = &mct_sc;
197 const uint64_t now = mct_gettime(sc);
198 int64_t delta = now - sc->sc_lastintr;
199 int64_t periods = delta / sc->sc_autoinc;
200
201 KASSERT(delta >= 0);
202 KASSERT(periods >= 0);
203
204 /* ack the interrupt */
205 mct_write_global(sc, MCT_G_INT_CSTAT, G_INT_CSTAT_CLEAR);
206
207 /* check if we missed clock interrupts */
208 if (periods > 1)
209 sc->sc_ev_missing_ticks.ev_count += periods - 1;
210
211 sc->sc_lastintr = now;
212 hardclock(cf);
213
214 /* handled */
215 return 1;
216 }
217
218 void
219 mct_init_cpu_clock(struct cpu_info *ci)
220 {
221 struct mct_softc * const sc = &mct_sc;
222 uint64_t now = mct_gettime(sc);
223 uint64_t then;
224 uint32_t tcon;
225
226 KASSERT(ci == curcpu());
227
228 sc->sc_lastintr = now;
229
230 /* get current config */
231 tcon = mct_read_global(sc, MCT_G_TCON);
232
233 /* setup auto increment */
234 mct_write_global(sc, MCT_G_COMP0_ADD_INCR, sc->sc_autoinc);
235
236 /* (re)setup comparator */
237 then = now + sc->sc_autoinc;
238 mct_write_global(sc, MCT_G_COMP0_L, (uint32_t) then);
239 mct_write_global(sc, MCT_G_COMP0_U, (uint32_t) (then >> 32));
240 tcon |= G_TCON_COMP0_AUTOINC;
241 tcon |= G_TCON_COMP0_ENABLE;
242
243 /* start timer */
244 tcon |= G_TCON_START;
245
246 /* enable interrupt */
247 mct_write_global(sc, MCT_G_INT_ENB, G_INT_ENB_ENABLE);
248
249 /* update config, starting the thing */
250 mct_write_global(sc, MCT_G_TCON, tcon);
251 }
252
253
254 #if 0
255 void
256 cpu_initclocks(void)
257 {
258 struct mct_softc * const sc = &mct_sc;
259
260 sc->sc_autoinc = sc->sc_freq / hz;
261 mct_init_cpu_clock(curcpu());
262
263 mct_timecounter.tc_name = device_xname(sc->sc_dev);
264 mct_timecounter.tc_frequency = sc->sc_freq;
265
266 tc_init(&mct_timecounter);
267
268 #if 0
269 {
270 uint64_t then, now;
271
272 printf("testing timer\n");
273 for (int i = 0; i < 200; i++) {
274 printf("cstat %d\n", mct_read_global(sc, MCT_G_INT_CSTAT));
275 then = mct_get_timecount(&mct_timecounter);
276 do {
277 now = mct_get_timecount(&mct_timecounter);
278 } while (now == then);
279 printf("\tgot %"PRIu64"\n", now);
280 for (int j = 0; j < 90000; j++);
281 }
282 printf("passed\n");
283 }
284 #endif
285 }
286
287 #endif
288