1 /* $NetBSD: bcm2835_tmr.c,v 1.12 2021/01/29 14:11:14 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 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 __KERNEL_RCSID(0, "$NetBSD: bcm2835_tmr.c,v 1.12 2021/01/29 14:11:14 skrll Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/device.h> 38 #include <sys/kernel.h> 39 #include <sys/timetc.h> 40 #include <sys/bus.h> 41 42 #include <arm/broadcom/bcm2835_intr.h> 43 #include <arm/broadcom/bcm2835reg.h> 44 #include <arm/broadcom/bcm2835var.h> 45 46 #include <dev/fdt/fdtvar.h> 47 48 #include <arm/fdt/arm_fdtvar.h> 49 50 /* Use the 3rd timer*/ 51 #define BCMTIMER 3 52 53 #define BCM2835_STIMER_CS 0x00 54 #define BCM2835_STIMER_M0 __BIT(0) 55 #define BCM2835_STIMER_M1 __BIT(1) 56 #define BCM2835_STIMER_M2 __BIT(2) 57 #define BCM2835_STIMER_M3 __BIT(3) 58 #define BCM2835_STIMER_CLO 0x04 59 #define BCM2835_STIMER_CHI 0x08 60 #define BCM2835_STIMER_C0 0x0c 61 #define BCM2835_STIMER_C1 0x10 62 #define BCM2835_STIMER_C2 0x14 63 #define BCM2835_STIMER_C3 0x18 64 65 #define BCM2835_STIMER_HZ 1000000 66 67 static const uint32_t counts_per_usec = (BCM2835_STIMER_HZ / 1000000); 68 static uint32_t counts_per_hz = ~0; 69 70 struct bcm2835tmr_softc { 71 device_t sc_dev; 72 73 bus_space_tag_t sc_iot; 74 bus_space_handle_t sc_ioh; 75 76 void *sc_ih; 77 }; 78 79 static int bcmtmr_match(device_t, cfdata_t, void *); 80 static void bcmtmr_attach(device_t, device_t, void *); 81 82 static int clockhandler(void *); 83 84 static u_int bcm2835tmr_get_timecount(struct timecounter *); 85 void bcm2835_tmr_setstatclockrate(int); 86 87 static struct bcm2835tmr_softc *bcm2835tmr_sc; 88 89 static struct timecounter bcm2835tmr_timecounter = { 90 .tc_get_timecount = bcm2835tmr_get_timecount, 91 .tc_poll_pps = 0, 92 .tc_counter_mask = ~0u, 93 .tc_frequency = BCM2835_STIMER_HZ, 94 .tc_name = NULL, /* set by attach */ 95 .tc_quality = 100, 96 .tc_priv = NULL, 97 .tc_next = NULL, 98 }; 99 100 CFATTACH_DECL_NEW(bcmtmr_fdt, sizeof(struct bcm2835tmr_softc), 101 bcmtmr_match, bcmtmr_attach, NULL, NULL); 102 103 static const struct device_compatible_entry compat_data[] = { 104 { .compat = "brcm,bcm2835-system-timer" }, 105 DEVICE_COMPAT_EOL 106 }; 107 108 /* ARGSUSED */ 109 static int 110 bcmtmr_match(device_t parent, cfdata_t match, void *aux) 111 { 112 struct fdt_attach_args * const faa = aux; 113 114 return of_compatible_match(faa->faa_phandle, compat_data); 115 } 116 117 static void 118 bcmtmr_attach(device_t parent, device_t self, void *aux) 119 { 120 struct bcm2835tmr_softc *sc = device_private(self); 121 struct fdt_attach_args * const faa = aux; 122 123 aprint_naive("\n"); 124 aprint_normal(": VC System Timer\n"); 125 126 if (bcm2835tmr_sc == NULL) 127 bcm2835tmr_sc = sc; 128 129 sc->sc_dev = self; 130 sc->sc_iot = faa->faa_bst; 131 const int phandle = faa->faa_phandle; 132 133 bus_addr_t addr; 134 bus_size_t size; 135 136 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 137 aprint_error(": missing 'reg' property\n"); 138 return; 139 } 140 141 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { 142 aprint_error_dev(sc->sc_dev, "unable to map device\n"); 143 return; 144 } 145 146 char intrstr[128]; 147 if (!fdtbus_intr_str(phandle, BCMTIMER, intrstr, sizeof(intrstr))) { 148 aprint_error(": failed to decode interrupt\n"); 149 return; 150 } 151 152 sc->sc_ih = fdtbus_intr_establish_xname(phandle, BCMTIMER, IPL_CLOCK, 153 FDT_INTR_MPSAFE, clockhandler, NULL, device_xname(self)); 154 if (sc->sc_ih == NULL) { 155 aprint_error(": failed to establish interrupt on %s\n", 156 intrstr); 157 return; 158 } 159 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 160 161 bcm2835tmr_timecounter.tc_name = device_xname(self); 162 } 163 164 void 165 cpu_initclocks(void) 166 { 167 struct bcm2835tmr_softc *sc = bcm2835tmr_sc; 168 uint32_t stcl; 169 170 KASSERT(sc != NULL); 171 172 bcm2835tmr_timecounter.tc_priv = sc; 173 174 counts_per_hz = BCM2835_STIMER_HZ / hz; 175 176 stcl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_CLO); 177 stcl += counts_per_hz; 178 179 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_C3, stcl); 180 181 tc_init(&bcm2835tmr_timecounter); 182 } 183 184 void 185 bcm2835_tmr_delay(unsigned int n) 186 { 187 struct bcm2835tmr_softc *sc = bcm2835tmr_sc; 188 uint32_t last, curr; 189 uint32_t delta, usecs; 190 191 KASSERT(sc != NULL); 192 193 last = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_CLO); 194 195 delta = usecs = 0; 196 while (n > usecs) { 197 curr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 198 BCM2835_STIMER_CLO); 199 200 /* Check to see if the timer has wrapped around. */ 201 if (curr < last) 202 delta += curr + (UINT32_MAX - last); 203 else 204 delta += curr - last; 205 206 last = curr; 207 208 if (delta >= counts_per_usec) { 209 usecs += delta / counts_per_usec; 210 delta %= counts_per_usec; 211 } 212 } 213 } 214 215 /* 216 * clockhandler: 217 * 218 * Handle the hardclock interrupt. 219 */ 220 static int 221 clockhandler(void *arg) 222 { 223 struct bcm2835tmr_softc *sc = bcm2835tmr_sc; 224 struct clockframe *frame = arg; 225 uint32_t curr, status; 226 227 status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 228 BCM2835_STIMER_CS); 229 230 if (!(status & BCM2835_STIMER_M3)) 231 return 0; 232 233 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_CS, 234 BCM2835_STIMER_M3); 235 236 hardclock(frame); 237 238 curr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_CLO); 239 240 curr += counts_per_hz; 241 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_C3, curr); 242 243 return 1; 244 } 245 246 void 247 setstatclockrate(int newhz) 248 { 249 } 250 251 static u_int 252 bcm2835tmr_get_timecount(struct timecounter *tc) 253 { 254 struct bcm2835tmr_softc *sc = tc->tc_priv; 255 256 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_STIMER_CLO); 257 } 258 259