1/* $NetBSD: mct.c,v 1.21 2022/03/03 06:26:29 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2014-2018 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 and Jared McNeill. 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 "opt_arm_timer.h" 33#include "opt_multiprocessor.h" 34 35#include <sys/cdefs.h> 36 37__KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.21 2022/03/03 06:26:29 riastradh Exp $"); 38 39#include <sys/param.h> 40#include <sys/bus.h> 41#include <sys/device.h> 42#include <sys/intr.h> 43#include <sys/kernel.h> 44#include <sys/proc.h> 45#include <sys/systm.h> 46#include <sys/timetc.h> 47#include <sys/kmem.h> 48 49#include <prop/proplib.h> 50 51#include <arm/samsung/exynos_reg.h> 52#include <arm/samsung/exynos_var.h> 53#include <arm/samsung/mct_reg.h> 54#include <arm/samsung/mct_var.h> 55 56#include <dev/fdt/fdtvar.h> 57#include <arm/fdt/arm_fdtvar.h> 58 59#if defined(MULTIPROCESSOR) 60#if !defined(__HAVE_GENERIC_CPU_INITCLOCKS) 61#error MULTIPROCESSOR kernels require __HAVE_GENERIC_CPU_INITCLOCKS 62#endif 63#include <arm/cortex/gtmr_intr.h> 64#include <arm/cortex/mpcore_var.h> 65#include <arm/cortex/gtmr_var.h> 66#endif 67 68static struct mct_softc mct_sc; 69 70static int mct_match(device_t, cfdata_t, void *); 71static void mct_attach(device_t, device_t, void *); 72 73static u_int mct_get_timecount(struct timecounter *); 74 75static struct timecounter mct_timecounter = { 76 .tc_get_timecount = mct_get_timecount, 77 .tc_counter_mask = ~0u, 78 .tc_frequency = EXYNOS_F_IN_FREQ, 79 .tc_name = "MCT", 80 .tc_quality = 400, 81 .tc_priv = &mct_sc, 82}; 83 84CFATTACH_DECL_NEW(exyo_mct, 0, mct_match, mct_attach, NULL, NULL); 85 86static inline uint32_t 87mct_read_global(struct mct_softc *sc, bus_size_t o) 88{ 89 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); 90} 91 92static inline void 93mct_write_global(struct mct_softc *sc, bus_size_t o, uint32_t v) 94{ 95 bus_size_t wreg; 96 uint32_t bit; 97 int i; 98 99 /* do the write */ 100 bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); 101// printf("%s: write %#x at %#x\n", 102// __func__, ((uint32_t) sc->sc_bsh + (uint32_t) o), v); 103 104 /* dependent on the write address, do the ack dance */ 105 if (o == MCT_G_CNT_L || o == MCT_G_CNT_U) { 106 wreg = MCT_G_CNT_WSTAT; 107 bit = (o == MCT_G_CNT_L) ? G_CNT_WSTAT_L : G_CNT_WSTAT_U; 108 } else { 109 switch (o) { 110 case MCT_G_COMP0_L: 111 wreg = MCT_G_WSTAT; 112 bit = G_WSTAT_COMP0_L; 113 break; 114 case MCT_G_COMP0_U: 115 wreg = MCT_G_WSTAT; 116 bit = G_WSTAT_COMP0_U; 117 break; 118 case MCT_G_COMP0_ADD_INCR: 119 wreg = MCT_G_WSTAT; 120 bit = G_WSTAT_ADD_INCR; 121 break; 122 case MCT_G_TCON: 123 wreg = MCT_G_WSTAT; 124 bit = G_WSTAT_TCON; 125 break; 126 case MCT_G_CNT_L: 127 wreg = MCT_G_CNT_WSTAT; 128 bit = G_CNT_WSTAT_L; 129 break; 130 case MCT_G_CNT_U: 131 wreg = MCT_G_CNT_WSTAT; 132 bit = G_CNT_WSTAT_U; 133 break; 134 default: 135 /* all other registers */ 136 return; 137 } 138 } 139 140 /* wait for ack */ 141 for (i = 0; i < 10000000; i++) { 142 /* value accepted by the hardware/hal ? */ 143 if (mct_read_global(sc, wreg) & bit) { 144 /* ack */ 145 bus_space_write_4(sc->sc_bst, sc->sc_bsh, wreg, bit); 146 return; 147 } 148 } 149 panic("MCT hangs after writing %#x at %#x", v, (uint32_t) o); 150} 151 152static int 153mct_intr(void *arg) 154{ 155 struct mct_softc * const sc = &mct_sc; 156 157 mct_write_global(sc, MCT_G_INT_CSTAT, G_INT_CSTAT_CLEAR); 158 159#if !defined(MULTIPROCESSOR) 160 hardclock(arg); 161#endif 162 163 return 1; 164} 165 166static u_int 167mct_get_timecount(struct timecounter *tc) 168{ 169 struct mct_softc * const sc = tc->tc_priv; 170 171 return mct_read_global(sc, MCT_G_CNT_L); 172} 173 174static uint64_t 175mct_read_gcnt(struct mct_softc *sc) 176{ 177 uint32_t gcntl, gcntu; 178 179 do { 180 gcntu = mct_read_global(sc, MCT_G_CNT_U); 181 gcntl = mct_read_global(sc, MCT_G_CNT_L); 182 } while (gcntu != mct_read_global(sc, MCT_G_CNT_U)); 183 184 return ((uint64_t)gcntu << 32) | gcntl; 185} 186 187static void 188mct_cpu_initclocks(void) 189{ 190 struct mct_softc * const sc = &mct_sc; 191 char intrstr[128]; 192 193 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 194 panic("%s: failed to decode interrupt", __func__); 195 196 sc->sc_global_ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK, 197 FDT_INTR_MPSAFE, mct_intr, NULL, device_xname(sc->sc_dev)); 198 if (sc->sc_global_ih == NULL) 199 panic("%s: failed to establish timer interrupt on %s", __func__, intrstr); 200 201 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 202 203 /* Start the timer */ 204 const u_int autoinc = sc->sc_freq / hz; 205 const uint64_t comp0 = mct_read_gcnt(sc) + autoinc; 206 207 mct_write_global(sc, MCT_G_TCON, G_TCON_START | G_TCON_COMP0_AUTOINC); 208 mct_write_global(sc, MCT_G_COMP0_ADD_INCR, autoinc); 209 mct_write_global(sc, MCT_G_COMP0_L, (uint32_t)comp0); 210 mct_write_global(sc, MCT_G_COMP0_U, (uint32_t)(comp0 >> 32)); 211 mct_write_global(sc, MCT_G_INT_ENB, G_INT_ENB_ENABLE); 212 mct_write_global(sc, MCT_G_TCON, G_TCON_START | G_TCON_COMP0_ENABLE | G_TCON_COMP0_AUTOINC); 213 214#if defined(MULTIPROCESSOR) 215 /* Initialize gtmr */ 216 gtmr_cpu_initclocks(); 217#endif 218} 219 220static void 221mct_fdt_cpu_hatch(void *priv, struct cpu_info *ci) 222{ 223#if defined(MULTIPROCESSOR) 224 gtmr_init_cpu_clock(ci); 225#endif 226} 227 228static const struct device_compatible_entry compat_data[] = { 229 { .compat = "samsung,exynos4210-mct" }, 230 DEVICE_COMPAT_EOL 231}; 232 233static int 234mct_match(device_t parent, cfdata_t cf, void *aux) 235{ 236 struct fdt_attach_args * const faa = aux; 237 238 return of_compatible_match(faa->faa_phandle, compat_data); 239} 240 241static void 242mct_attach(device_t parent, device_t self, void *aux) 243{ 244 struct mct_softc * const sc = &mct_sc; 245 struct fdt_attach_args * const faa = aux; 246 bus_addr_t addr; 247 bus_size_t size; 248 int error; 249 250 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) { 251 aprint_error(": couldn't get registers\n"); 252 return; 253 } 254 255 device_set_private(self, sc); 256 sc->sc_dev = self; 257 sc->sc_phandle = faa->faa_phandle; 258 sc->sc_bst = faa->faa_bst; 259 sc->sc_freq = EXYNOS_F_IN_FREQ; 260 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 261 if (error) { 262 aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", 263 addr, error); 264 return; 265 } 266 267 aprint_naive("\n"); 268 aprint_normal(": Exynos SoC multi core timer (64 bits)\n"); 269 270 tc_init(&mct_timecounter); 271 272 arm_fdt_cpu_hatch_register(self, mct_fdt_cpu_hatch); 273 arm_fdt_timer_register(mct_cpu_initclocks); 274 275#if defined(MULTIPROCESSOR) 276 /* Start the timer */ 277 mct_write_global(sc, MCT_G_TCON, G_TCON_START); 278 279 struct mpcore_attach_args mpcaa = { 280 .mpcaa_name = "armgtmr", 281 .mpcaa_irq = IRQ_GTMR_PPI_VTIMER, 282 }; 283 config_found(self, &mpcaa, NULL, CFARGS_NONE); 284#endif 285} 286 287void 288mct_delay(u_int n) 289{ 290 struct mct_softc * const sc = &mct_sc; 291 uint64_t cur, prev; 292 293 if (sc->sc_bsh == 0) 294 panic("%s: mct driver not attached", __func__); 295 296 const long incs_per_us = sc->sc_freq / 1000000; 297 long ticks = n * incs_per_us; 298 299 prev = mct_read_gcnt(sc); 300 while (ticks > 0) { 301 cur = mct_read_gcnt(sc); 302 if (cur > prev) 303 ticks -= (cur - prev); 304 else 305 ticks -= (UINT64_MAX - cur + prev); 306 prev = cur; 307 } 308} 309