bcm2835_systimer.c revision 330897
1/* 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 5 * Copyright (c) 2012 Damjan Marion <dmarion@freebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/arm/broadcom/bcm2835/bcm2835_systimer.c 330897 2018-03-14 03:19:51Z eadler $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/malloc.h> 39#include <sys/rman.h> 40#include <sys/timeet.h> 41#include <sys/timetc.h> 42#include <sys/watchdog.h> 43#include <machine/bus.h> 44#include <machine/cpu.h> 45#include <machine/intr.h> 46 47#include <dev/fdt/fdt_common.h> 48#include <dev/ofw/openfirm.h> 49#include <dev/ofw/ofw_bus.h> 50#include <dev/ofw/ofw_bus_subr.h> 51 52#include <machine/bus.h> 53 54#define BCM2835_NUM_TIMERS 4 55 56#define DEFAULT_TIMER 3 57#define DEFAULT_TIMER_NAME "BCM2835-3" 58#define DEFAULT_FREQUENCY 1000000 59#define MIN_PERIOD 5LLU 60 61#define SYSTIMER_CS 0x00 62#define SYSTIMER_CLO 0x04 63#define SYSTIMER_CHI 0x08 64#define SYSTIMER_C0 0x0C 65#define SYSTIMER_C1 0x10 66#define SYSTIMER_C2 0x14 67#define SYSTIMER_C3 0x18 68 69struct systimer { 70 int index; 71 bool enabled; 72 struct eventtimer et; 73}; 74 75struct bcm_systimer_softc { 76 struct resource* mem_res; 77 struct resource* irq_res[BCM2835_NUM_TIMERS]; 78 void* intr_hl[BCM2835_NUM_TIMERS]; 79 uint32_t sysclk_freq; 80 bus_space_tag_t bst; 81 bus_space_handle_t bsh; 82 struct systimer st[BCM2835_NUM_TIMERS]; 83}; 84 85static struct resource_spec bcm_systimer_irq_spec[] = { 86 { SYS_RES_IRQ, 0, RF_ACTIVE }, 87 { SYS_RES_IRQ, 1, RF_ACTIVE }, 88 { SYS_RES_IRQ, 2, RF_ACTIVE }, 89 { SYS_RES_IRQ, 3, RF_ACTIVE }, 90 { -1, 0, 0 } 91}; 92 93static struct bcm_systimer_softc *bcm_systimer_sc = NULL; 94 95/* Read/Write macros for Timer used as timecounter */ 96#define bcm_systimer_tc_read_4(reg) \ 97 bus_space_read_4(bcm_systimer_sc->bst, \ 98 bcm_systimer_sc->bsh, reg) 99 100#define bcm_systimer_tc_write_4(reg, val) \ 101 bus_space_write_4(bcm_systimer_sc->bst, \ 102 bcm_systimer_sc->bsh, reg, val) 103 104static unsigned bcm_systimer_tc_get_timecount(struct timecounter *); 105 106static struct timecounter bcm_systimer_tc = { 107 .tc_name = DEFAULT_TIMER_NAME, 108 .tc_get_timecount = bcm_systimer_tc_get_timecount, 109 .tc_poll_pps = NULL, 110 .tc_counter_mask = ~0u, 111 .tc_frequency = 0, 112 .tc_quality = 1000, 113}; 114 115static unsigned 116bcm_systimer_tc_get_timecount(struct timecounter *tc) 117{ 118 return bcm_systimer_tc_read_4(SYSTIMER_CLO); 119} 120 121static int 122bcm_systimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 123{ 124 struct systimer *st = et->et_priv; 125 uint32_t clo, clo1; 126 uint32_t count; 127 register_t s; 128 129 if (first != 0) { 130 131 count = ((uint32_t)et->et_frequency * first) >> 32; 132 133 s = intr_disable(); 134 clo = bcm_systimer_tc_read_4(SYSTIMER_CLO); 135restart: 136 clo += count; 137 /* 138 * Clear pending interrupts 139 */ 140 bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index)); 141 bcm_systimer_tc_write_4(SYSTIMER_C0 + st->index*4, clo); 142 clo1 = bcm_systimer_tc_read_4(SYSTIMER_CLO); 143 if ((int32_t)(clo1 - clo) >= 0) { 144 count *= 2; 145 clo = clo1; 146 goto restart; 147 } 148 st->enabled = 1; 149 intr_restore(s); 150 151 return (0); 152 } 153 154 return (EINVAL); 155} 156 157static int 158bcm_systimer_stop(struct eventtimer *et) 159{ 160 struct systimer *st = et->et_priv; 161 st->enabled = 0; 162 163 return (0); 164} 165 166static int 167bcm_systimer_intr(void *arg) 168{ 169 struct systimer *st = (struct systimer *)arg; 170 uint32_t cs; 171 172 cs = bcm_systimer_tc_read_4(SYSTIMER_CS); 173 if ((cs & (1 << st->index)) == 0) 174 return (FILTER_STRAY); 175 176 /* ACK interrupt */ 177 bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index)); 178 if (st->enabled) { 179 if (st->et.et_active) { 180 st->et.et_event_cb(&st->et, st->et.et_arg); 181 } 182 } 183 184 return (FILTER_HANDLED); 185} 186 187static int 188bcm_systimer_probe(device_t dev) 189{ 190 191 if (!ofw_bus_status_okay(dev)) 192 return (ENXIO); 193 194 if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-system-timer")) { 195 device_set_desc(dev, "BCM2835 System Timer"); 196 return (BUS_PROBE_DEFAULT); 197 } 198 199 return (ENXIO); 200} 201 202static int 203bcm_systimer_attach(device_t dev) 204{ 205 struct bcm_systimer_softc *sc = device_get_softc(dev); 206 int err; 207 int rid = 0; 208 209 if (bcm_systimer_sc != NULL) 210 return (EINVAL); 211 212 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 213 if (sc->mem_res == NULL) { 214 device_printf(dev, "could not allocate memory resource\n"); 215 return (ENXIO); 216 } 217 218 sc->bst = rman_get_bustag(sc->mem_res); 219 sc->bsh = rman_get_bushandle(sc->mem_res); 220 221 /* Request the IRQ resources */ 222 err = bus_alloc_resources(dev, bcm_systimer_irq_spec, 223 sc->irq_res); 224 if (err) { 225 device_printf(dev, "Error: could not allocate irq resources\n"); 226 return (ENXIO); 227 } 228 229 /* TODO: get frequency from FDT */ 230 sc->sysclk_freq = DEFAULT_FREQUENCY; 231 232 /* Setup and enable the timer */ 233 if (bus_setup_intr(dev, sc->irq_res[DEFAULT_TIMER], INTR_TYPE_CLK, 234 bcm_systimer_intr, NULL, &sc->st[DEFAULT_TIMER], 235 &sc->intr_hl[DEFAULT_TIMER]) != 0) { 236 bus_release_resources(dev, bcm_systimer_irq_spec, 237 sc->irq_res); 238 device_printf(dev, "Unable to setup the clock irq handler.\n"); 239 return (ENXIO); 240 } 241 242 sc->st[DEFAULT_TIMER].index = DEFAULT_TIMER; 243 sc->st[DEFAULT_TIMER].enabled = 0; 244 sc->st[DEFAULT_TIMER].et.et_name = DEFAULT_TIMER_NAME; 245 sc->st[DEFAULT_TIMER].et.et_flags = ET_FLAGS_ONESHOT; 246 sc->st[DEFAULT_TIMER].et.et_quality = 1000; 247 sc->st[DEFAULT_TIMER].et.et_frequency = sc->sysclk_freq; 248 sc->st[DEFAULT_TIMER].et.et_min_period = 249 (MIN_PERIOD << 32) / sc->st[DEFAULT_TIMER].et.et_frequency + 1; 250 sc->st[DEFAULT_TIMER].et.et_max_period = 251 (0x7ffffffeLLU << 32) / sc->st[DEFAULT_TIMER].et.et_frequency; 252 sc->st[DEFAULT_TIMER].et.et_start = bcm_systimer_start; 253 sc->st[DEFAULT_TIMER].et.et_stop = bcm_systimer_stop; 254 sc->st[DEFAULT_TIMER].et.et_priv = &sc->st[DEFAULT_TIMER]; 255 et_register(&sc->st[DEFAULT_TIMER].et); 256 257 bcm_systimer_sc = sc; 258 259 bcm_systimer_tc.tc_frequency = DEFAULT_FREQUENCY; 260 tc_init(&bcm_systimer_tc); 261 262 return (0); 263} 264 265static device_method_t bcm_systimer_methods[] = { 266 DEVMETHOD(device_probe, bcm_systimer_probe), 267 DEVMETHOD(device_attach, bcm_systimer_attach), 268 { 0, 0 } 269}; 270 271static driver_t bcm_systimer_driver = { 272 "systimer", 273 bcm_systimer_methods, 274 sizeof(struct bcm_systimer_softc), 275}; 276 277static devclass_t bcm_systimer_devclass; 278 279DRIVER_MODULE(bcm_systimer, simplebus, bcm_systimer_driver, bcm_systimer_devclass, 0, 0); 280 281void 282DELAY(int usec) 283{ 284 int32_t counts; 285 uint32_t first, last; 286 287 if (bcm_systimer_sc == NULL) { 288 for (; usec > 0; usec--) 289 for (counts = 200; counts > 0; counts--) 290 /* Prevent gcc from optimizing out the loop */ 291 cpufunc_nullop(); 292 return; 293 } 294 295 /* Get the number of times to count */ 296 counts = usec * (bcm_systimer_tc.tc_frequency / 1000000) + 1; 297 298 first = bcm_systimer_tc_read_4(SYSTIMER_CLO); 299 300 while (counts > 0) { 301 last = bcm_systimer_tc_read_4(SYSTIMER_CLO); 302 if (last == first) 303 continue; 304 if (last>first) { 305 counts -= (int32_t)(last - first); 306 } else { 307 counts -= (int32_t)((0xFFFFFFFF - first) + last); 308 } 309 first = last; 310 } 311} 312