1 1.21 riastrad /* $NetBSD: mct.c,v 1.21 2022/03/03 06:26:29 riastradh Exp $ */ 2 1.1 matt 3 1.1 matt /*- 4 1.14 jmcneill * Copyright (c) 2014-2018 The NetBSD Foundation, Inc. 5 1.1 matt * All rights reserved. 6 1.1 matt * 7 1.1 matt * This code is derived from software contributed to The NetBSD Foundation 8 1.14 jmcneill * by Reinoud Zandijk and Jared McNeill. 9 1.1 matt * 10 1.1 matt * Redistribution and use in source and binary forms, with or without 11 1.1 matt * modification, are permitted provided that the following conditions 12 1.1 matt * are met: 13 1.1 matt * 1. Redistributions of source code must retain the above copyright 14 1.1 matt * notice, this list of conditions and the following disclaimer. 15 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 matt * notice, this list of conditions and the following disclaimer in the 17 1.1 matt * documentation and/or other materials provided with the distribution. 18 1.1 matt * 19 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 matt * POSSIBILITY OF SUCH DAMAGE. 30 1.1 matt */ 31 1.1 matt 32 1.15 jmcneill #include "opt_arm_timer.h" 33 1.15 jmcneill #include "opt_multiprocessor.h" 34 1.15 jmcneill 35 1.1 matt #include <sys/cdefs.h> 36 1.1 matt 37 1.21 riastrad __KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.21 2022/03/03 06:26:29 riastradh Exp $"); 38 1.1 matt 39 1.1 matt #include <sys/param.h> 40 1.1 matt #include <sys/bus.h> 41 1.1 matt #include <sys/device.h> 42 1.1 matt #include <sys/intr.h> 43 1.1 matt #include <sys/kernel.h> 44 1.1 matt #include <sys/proc.h> 45 1.1 matt #include <sys/systm.h> 46 1.1 matt #include <sys/timetc.h> 47 1.9 marty #include <sys/kmem.h> 48 1.1 matt 49 1.1 matt #include <prop/proplib.h> 50 1.1 matt 51 1.1 matt #include <arm/samsung/exynos_reg.h> 52 1.1 matt #include <arm/samsung/exynos_var.h> 53 1.1 matt #include <arm/samsung/mct_reg.h> 54 1.1 matt #include <arm/samsung/mct_var.h> 55 1.1 matt 56 1.7 marty #include <dev/fdt/fdtvar.h> 57 1.12 jmcneill #include <arm/fdt/arm_fdtvar.h> 58 1.1 matt 59 1.15 jmcneill #if defined(MULTIPROCESSOR) 60 1.15 jmcneill #if !defined(__HAVE_GENERIC_CPU_INITCLOCKS) 61 1.15 jmcneill #error MULTIPROCESSOR kernels require __HAVE_GENERIC_CPU_INITCLOCKS 62 1.15 jmcneill #endif 63 1.15 jmcneill #include <arm/cortex/gtmr_intr.h> 64 1.15 jmcneill #include <arm/cortex/mpcore_var.h> 65 1.15 jmcneill #include <arm/cortex/gtmr_var.h> 66 1.15 jmcneill #endif 67 1.15 jmcneill 68 1.14 jmcneill static struct mct_softc mct_sc; 69 1.14 jmcneill 70 1.1 matt static int mct_match(device_t, cfdata_t, void *); 71 1.1 matt static void mct_attach(device_t, device_t, void *); 72 1.1 matt 73 1.14 jmcneill static u_int mct_get_timecount(struct timecounter *); 74 1.14 jmcneill 75 1.14 jmcneill static struct timecounter mct_timecounter = { 76 1.14 jmcneill .tc_get_timecount = mct_get_timecount, 77 1.14 jmcneill .tc_counter_mask = ~0u, 78 1.14 jmcneill .tc_frequency = EXYNOS_F_IN_FREQ, 79 1.14 jmcneill .tc_name = "MCT", 80 1.15 jmcneill .tc_quality = 400, 81 1.14 jmcneill .tc_priv = &mct_sc, 82 1.14 jmcneill }; 83 1.14 jmcneill 84 1.1 matt CFATTACH_DECL_NEW(exyo_mct, 0, mct_match, mct_attach, NULL, NULL); 85 1.1 matt 86 1.1 matt static inline uint32_t 87 1.1 matt mct_read_global(struct mct_softc *sc, bus_size_t o) 88 1.1 matt { 89 1.1 matt return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); 90 1.1 matt } 91 1.1 matt 92 1.1 matt static inline void 93 1.1 matt mct_write_global(struct mct_softc *sc, bus_size_t o, uint32_t v) 94 1.1 matt { 95 1.1 matt bus_size_t wreg; 96 1.1 matt uint32_t bit; 97 1.1 matt int i; 98 1.1 matt 99 1.1 matt /* do the write */ 100 1.1 matt bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); 101 1.1 matt // printf("%s: write %#x at %#x\n", 102 1.1 matt // __func__, ((uint32_t) sc->sc_bsh + (uint32_t) o), v); 103 1.1 matt 104 1.1 matt /* dependent on the write address, do the ack dance */ 105 1.1 matt if (o == MCT_G_CNT_L || o == MCT_G_CNT_U) { 106 1.1 matt wreg = MCT_G_CNT_WSTAT; 107 1.1 matt bit = (o == MCT_G_CNT_L) ? G_CNT_WSTAT_L : G_CNT_WSTAT_U; 108 1.1 matt } else { 109 1.1 matt switch (o) { 110 1.1 matt case MCT_G_COMP0_L: 111 1.14 jmcneill wreg = MCT_G_WSTAT; 112 1.1 matt bit = G_WSTAT_COMP0_L; 113 1.1 matt break; 114 1.1 matt case MCT_G_COMP0_U: 115 1.14 jmcneill wreg = MCT_G_WSTAT; 116 1.1 matt bit = G_WSTAT_COMP0_U; 117 1.1 matt break; 118 1.1 matt case MCT_G_COMP0_ADD_INCR: 119 1.14 jmcneill wreg = MCT_G_WSTAT; 120 1.1 matt bit = G_WSTAT_ADD_INCR; 121 1.1 matt break; 122 1.1 matt case MCT_G_TCON: 123 1.14 jmcneill wreg = MCT_G_WSTAT; 124 1.1 matt bit = G_WSTAT_TCON; 125 1.1 matt break; 126 1.14 jmcneill case MCT_G_CNT_L: 127 1.14 jmcneill wreg = MCT_G_CNT_WSTAT; 128 1.14 jmcneill bit = G_CNT_WSTAT_L; 129 1.14 jmcneill break; 130 1.14 jmcneill case MCT_G_CNT_U: 131 1.14 jmcneill wreg = MCT_G_CNT_WSTAT; 132 1.14 jmcneill bit = G_CNT_WSTAT_U; 133 1.14 jmcneill break; 134 1.1 matt default: 135 1.1 matt /* all other registers */ 136 1.1 matt return; 137 1.1 matt } 138 1.1 matt } 139 1.1 matt 140 1.1 matt /* wait for ack */ 141 1.1 matt for (i = 0; i < 10000000; i++) { 142 1.1 matt /* value accepted by the hardware/hal ? */ 143 1.1 matt if (mct_read_global(sc, wreg) & bit) { 144 1.1 matt /* ack */ 145 1.1 matt bus_space_write_4(sc->sc_bst, sc->sc_bsh, wreg, bit); 146 1.1 matt return; 147 1.1 matt } 148 1.1 matt } 149 1.1 matt panic("MCT hangs after writing %#x at %#x", v, (uint32_t) o); 150 1.1 matt } 151 1.1 matt 152 1.14 jmcneill static int 153 1.14 jmcneill mct_intr(void *arg) 154 1.14 jmcneill { 155 1.14 jmcneill struct mct_softc * const sc = &mct_sc; 156 1.14 jmcneill 157 1.14 jmcneill mct_write_global(sc, MCT_G_INT_CSTAT, G_INT_CSTAT_CLEAR); 158 1.14 jmcneill 159 1.15 jmcneill #if !defined(MULTIPROCESSOR) 160 1.15 jmcneill hardclock(arg); 161 1.15 jmcneill #endif 162 1.14 jmcneill 163 1.14 jmcneill return 1; 164 1.14 jmcneill } 165 1.14 jmcneill 166 1.14 jmcneill static u_int 167 1.14 jmcneill mct_get_timecount(struct timecounter *tc) 168 1.14 jmcneill { 169 1.14 jmcneill struct mct_softc * const sc = tc->tc_priv; 170 1.14 jmcneill 171 1.14 jmcneill return mct_read_global(sc, MCT_G_CNT_L); 172 1.14 jmcneill } 173 1.14 jmcneill 174 1.14 jmcneill static uint64_t 175 1.14 jmcneill mct_read_gcnt(struct mct_softc *sc) 176 1.14 jmcneill { 177 1.14 jmcneill uint32_t gcntl, gcntu; 178 1.14 jmcneill 179 1.14 jmcneill do { 180 1.14 jmcneill gcntu = mct_read_global(sc, MCT_G_CNT_U); 181 1.14 jmcneill gcntl = mct_read_global(sc, MCT_G_CNT_L); 182 1.14 jmcneill } while (gcntu != mct_read_global(sc, MCT_G_CNT_U)); 183 1.14 jmcneill 184 1.14 jmcneill return ((uint64_t)gcntu << 32) | gcntl; 185 1.14 jmcneill } 186 1.14 jmcneill 187 1.14 jmcneill static void 188 1.14 jmcneill mct_cpu_initclocks(void) 189 1.14 jmcneill { 190 1.14 jmcneill struct mct_softc * const sc = &mct_sc; 191 1.14 jmcneill char intrstr[128]; 192 1.14 jmcneill 193 1.14 jmcneill if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 194 1.14 jmcneill panic("%s: failed to decode interrupt", __func__); 195 1.14 jmcneill 196 1.18 skrll sc->sc_global_ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK, 197 1.18 skrll FDT_INTR_MPSAFE, mct_intr, NULL, device_xname(sc->sc_dev)); 198 1.14 jmcneill if (sc->sc_global_ih == NULL) 199 1.14 jmcneill panic("%s: failed to establish timer interrupt on %s", __func__, intrstr); 200 1.14 jmcneill 201 1.14 jmcneill aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 202 1.14 jmcneill 203 1.14 jmcneill /* Start the timer */ 204 1.14 jmcneill const u_int autoinc = sc->sc_freq / hz; 205 1.14 jmcneill const uint64_t comp0 = mct_read_gcnt(sc) + autoinc; 206 1.14 jmcneill 207 1.14 jmcneill mct_write_global(sc, MCT_G_TCON, G_TCON_START | G_TCON_COMP0_AUTOINC); 208 1.14 jmcneill mct_write_global(sc, MCT_G_COMP0_ADD_INCR, autoinc); 209 1.14 jmcneill mct_write_global(sc, MCT_G_COMP0_L, (uint32_t)comp0); 210 1.14 jmcneill mct_write_global(sc, MCT_G_COMP0_U, (uint32_t)(comp0 >> 32)); 211 1.14 jmcneill mct_write_global(sc, MCT_G_INT_ENB, G_INT_ENB_ENABLE); 212 1.14 jmcneill mct_write_global(sc, MCT_G_TCON, G_TCON_START | G_TCON_COMP0_ENABLE | G_TCON_COMP0_AUTOINC); 213 1.15 jmcneill 214 1.15 jmcneill #if defined(MULTIPROCESSOR) 215 1.15 jmcneill /* Initialize gtmr */ 216 1.15 jmcneill gtmr_cpu_initclocks(); 217 1.15 jmcneill #endif 218 1.15 jmcneill } 219 1.15 jmcneill 220 1.15 jmcneill static void 221 1.15 jmcneill mct_fdt_cpu_hatch(void *priv, struct cpu_info *ci) 222 1.15 jmcneill { 223 1.15 jmcneill #if defined(MULTIPROCESSOR) 224 1.15 jmcneill gtmr_init_cpu_clock(ci); 225 1.15 jmcneill #endif 226 1.12 jmcneill } 227 1.12 jmcneill 228 1.17 thorpej static const struct device_compatible_entry compat_data[] = { 229 1.17 thorpej { .compat = "samsung,exynos4210-mct" }, 230 1.17 thorpej DEVICE_COMPAT_EOL 231 1.17 thorpej }; 232 1.17 thorpej 233 1.1 matt static int 234 1.1 matt mct_match(device_t parent, cfdata_t cf, void *aux) 235 1.1 matt { 236 1.17 thorpej struct fdt_attach_args * const faa = aux; 237 1.1 matt 238 1.17 thorpej return of_compatible_match(faa->faa_phandle, compat_data); 239 1.1 matt } 240 1.1 matt 241 1.1 matt static void 242 1.1 matt mct_attach(device_t parent, device_t self, void *aux) 243 1.1 matt { 244 1.1 matt struct mct_softc * const sc = &mct_sc; 245 1.7 marty struct fdt_attach_args * const faa = aux; 246 1.7 marty bus_addr_t addr; 247 1.7 marty bus_size_t size; 248 1.7 marty int error; 249 1.7 marty 250 1.7 marty if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) { 251 1.7 marty aprint_error(": couldn't get registers\n"); 252 1.7 marty return; 253 1.7 marty } 254 1.1 matt 255 1.21 riastrad device_set_private(self, sc); 256 1.1 matt sc->sc_dev = self; 257 1.14 jmcneill sc->sc_phandle = faa->faa_phandle; 258 1.7 marty sc->sc_bst = faa->faa_bst; 259 1.11 jmcneill sc->sc_freq = EXYNOS_F_IN_FREQ; 260 1.7 marty error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 261 1.7 marty if (error) { 262 1.16 skrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", 263 1.16 skrll addr, error); 264 1.7 marty return; 265 1.7 marty } 266 1.1 matt 267 1.1 matt aprint_naive("\n"); 268 1.11 jmcneill aprint_normal(": Exynos SoC multi core timer (64 bits)\n"); 269 1.1 matt 270 1.14 jmcneill tc_init(&mct_timecounter); 271 1.14 jmcneill 272 1.14 jmcneill arm_fdt_cpu_hatch_register(self, mct_fdt_cpu_hatch); 273 1.14 jmcneill arm_fdt_timer_register(mct_cpu_initclocks); 274 1.15 jmcneill 275 1.15 jmcneill #if defined(MULTIPROCESSOR) 276 1.15 jmcneill /* Start the timer */ 277 1.15 jmcneill mct_write_global(sc, MCT_G_TCON, G_TCON_START); 278 1.15 jmcneill 279 1.15 jmcneill struct mpcore_attach_args mpcaa = { 280 1.15 jmcneill .mpcaa_name = "armgtmr", 281 1.15 jmcneill .mpcaa_irq = IRQ_GTMR_PPI_VTIMER, 282 1.15 jmcneill }; 283 1.20 thorpej config_found(self, &mpcaa, NULL, CFARGS_NONE); 284 1.15 jmcneill #endif 285 1.14 jmcneill } 286 1.14 jmcneill 287 1.14 jmcneill void 288 1.14 jmcneill mct_delay(u_int n) 289 1.14 jmcneill { 290 1.14 jmcneill struct mct_softc * const sc = &mct_sc; 291 1.14 jmcneill uint64_t cur, prev; 292 1.14 jmcneill 293 1.14 jmcneill if (sc->sc_bsh == 0) 294 1.14 jmcneill panic("%s: mct driver not attached", __func__); 295 1.1 matt 296 1.14 jmcneill const long incs_per_us = sc->sc_freq / 1000000; 297 1.14 jmcneill long ticks = n * incs_per_us; 298 1.1 matt 299 1.14 jmcneill prev = mct_read_gcnt(sc); 300 1.14 jmcneill while (ticks > 0) { 301 1.14 jmcneill cur = mct_read_gcnt(sc); 302 1.14 jmcneill if (cur > prev) 303 1.14 jmcneill ticks -= (cur - prev); 304 1.14 jmcneill else 305 1.14 jmcneill ticks -= (UINT64_MAX - cur + prev); 306 1.14 jmcneill prev = cur; 307 1.14 jmcneill } 308 1.11 jmcneill } 309