1239281Sgonzo/*- 2239281Sgonzo * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org> 3239281Sgonzo * All rights reserved. 4239281Sgonzo * 5239281Sgonzo * Redistribution and use in source and binary forms, with or without 6239281Sgonzo * modification, are permitted provided that the following conditions 7239281Sgonzo * are met: 8239281Sgonzo * 1. Redistributions of source code must retain the above copyright 9239281Sgonzo * notice, this list of conditions and the following disclaimer. 10239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11239281Sgonzo * notice, this list of conditions and the following disclaimer in the 12239281Sgonzo * documentation and/or other materials provided with the distribution. 13239281Sgonzo * 14239281Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239281Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239281Sgonzo * SUCH DAMAGE. 25239281Sgonzo */ 26239281Sgonzo 27239281Sgonzo#include <sys/cdefs.h> 28239281Sgonzo__FBSDID("$FreeBSD: stable/11/sys/arm/ti/am335x/am335x_dmtimer.c 331907 2018-04-03 06:06:39Z gonzo $"); 29239281Sgonzo 30239281Sgonzo#include <sys/param.h> 31239281Sgonzo#include <sys/systm.h> 32239281Sgonzo#include <sys/bus.h> 33239281Sgonzo#include <sys/kernel.h> 34239281Sgonzo#include <sys/module.h> 35239281Sgonzo#include <sys/malloc.h> 36239281Sgonzo#include <sys/rman.h> 37239281Sgonzo#include <sys/timeet.h> 38239281Sgonzo#include <sys/timetc.h> 39239281Sgonzo#include <machine/bus.h> 40239281Sgonzo 41331907Sgonzo#ifdef MULTIDELAY 42331907Sgonzo#include <machine/machdep.h> /* For arm_set_delay */ 43331907Sgonzo#endif 44331907Sgonzo 45239281Sgonzo#include <dev/ofw/openfirm.h> 46239281Sgonzo#include <dev/ofw/ofw_bus.h> 47239281Sgonzo#include <dev/ofw/ofw_bus_subr.h> 48239281Sgonzo 49239281Sgonzo#include <arm/ti/ti_prcm.h> 50283276Sgonzo#include <arm/ti/ti_hwmods.h> 51239281Sgonzo 52286696Sian#include "am335x_dmtreg.h" 53239281Sgonzo 54239281Sgonzostruct am335x_dmtimer_softc { 55283276Sgonzo device_t dev; 56283276Sgonzo int tmr_mem_rid; 57283276Sgonzo struct resource * tmr_mem_res; 58283276Sgonzo int tmr_irq_rid; 59283276Sgonzo struct resource * tmr_irq_res; 60283276Sgonzo void *tmr_irq_handler; 61239281Sgonzo uint32_t sysclk_freq; 62283276Sgonzo uint32_t tclr; /* Cached TCLR register. */ 63283276Sgonzo union { 64283276Sgonzo struct timecounter tc; 65283276Sgonzo struct eventtimer et; 66283276Sgonzo } func; 67286696Sian int tmr_num; /* Hardware unit number. */ 68286696Sian char tmr_name[12]; /* "DMTimerN", N = tmr_num */ 69239281Sgonzo}; 70239281Sgonzo 71283276Sgonzostatic struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL; 72283276Sgonzostatic struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL; 73239281Sgonzo 74331907Sgonzostatic void am335x_dmtimer_delay(int, void *); 75331907Sgonzo 76259750Sian/* 77286696Sian * We use dmtimer2 for eventtimer and dmtimer3 for timecounter. 78259750Sian */ 79286696Sian#define ET_TMR_NUM 2 80286696Sian#define TC_TMR_NUM 3 81259750Sian 82286696Sian/* List of compatible strings for FDT tree */ 83286696Sianstatic struct ofw_compat_data compat_data[] = { 84286696Sian {"ti,am335x-timer", 1}, 85286696Sian {"ti,am335x-timer-1ms", 1}, 86286696Sian {NULL, 0}, 87259750Sian}; 88259750Sian 89286696Sian#define DMTIMER_READ4(sc, reg) bus_read_4((sc)->tmr_mem_res, (reg)) 90286696Sian#define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->tmr_mem_res, (reg), (val)) 91259750Sian 92239281Sgonzostatic int 93286696Sianam335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 94239281Sgonzo{ 95259744Sian struct am335x_dmtimer_softc *sc; 96259750Sian uint32_t initial_count, reload_count; 97239281Sgonzo 98259744Sian sc = et->et_priv; 99259744Sian 100259750Sian /* 101259750Sian * Stop the timer before changing it. This routine will often be called 102259750Sian * while the timer is still running, to either lengthen or shorten the 103259750Sian * current event time. We need to ensure the timer doesn't expire while 104259750Sian * we're working with it. 105259750Sian * 106259750Sian * Also clear any pending interrupt status, because it's at least 107259750Sian * theoretically possible that we're running in a primary interrupt 108259750Sian * context now, and a timer interrupt could be pending even before we 109259750Sian * stopped the timer. The more likely case is that we're being called 110259750Sian * from the et_event_cb() routine dispatched from our own handler, but 111259750Sian * it's not clear to me that that's the only case possible. 112259750Sian */ 113283276Sgonzo sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); 114283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); 115283276Sgonzo DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); 116259750Sian 117247463Smav if (period != 0) { 118259750Sian reload_count = ((uint32_t)et->et_frequency * period) >> 32; 119283276Sgonzo sc->tclr |= DMT_TCLR_AUTOLOAD; 120239281Sgonzo } else { 121259750Sian reload_count = 0; 122239281Sgonzo } 123239281Sgonzo 124247463Smav if (first != 0) 125259750Sian initial_count = ((uint32_t)et->et_frequency * first) >> 32; 126247463Smav else 127259750Sian initial_count = reload_count; 128239281Sgonzo 129259750Sian /* 130259750Sian * Set auto-reload and current-count values. This timer hardware counts 131259750Sian * up from the initial/reload value and interrupts on the zero rollover. 132259750Sian */ 133283276Sgonzo DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); 134283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); 135239281Sgonzo 136259750Sian /* Enable overflow interrupt, and start the timer. */ 137283276Sgonzo DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); 138283276Sgonzo sc->tclr |= DMT_TCLR_START; 139283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); 140239281Sgonzo 141239281Sgonzo return (0); 142239281Sgonzo} 143239281Sgonzo 144239281Sgonzostatic int 145286696Sianam335x_dmtimer_et_stop(struct eventtimer *et) 146239281Sgonzo{ 147259744Sian struct am335x_dmtimer_softc *sc; 148239281Sgonzo 149259744Sian sc = et->et_priv; 150259744Sian 151259750Sian /* Stop timer, disable and clear interrupt. */ 152283276Sgonzo sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); 153283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); 154283276Sgonzo DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); 155283276Sgonzo DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); 156239281Sgonzo return (0); 157239281Sgonzo} 158239281Sgonzo 159239281Sgonzostatic int 160286696Sianam335x_dmtimer_et_intr(void *arg) 161239281Sgonzo{ 162259744Sian struct am335x_dmtimer_softc *sc; 163239281Sgonzo 164259744Sian sc = arg; 165259750Sian 166259750Sian /* Ack the interrupt, and invoke the callback if it's still enabled. */ 167283276Sgonzo DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); 168283276Sgonzo if (sc->func.et.et_active) 169283276Sgonzo sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg); 170239281Sgonzo 171239281Sgonzo return (FILTER_HANDLED); 172239281Sgonzo} 173239281Sgonzo 174239281Sgonzostatic int 175286696Sianam335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc) 176283276Sgonzo{ 177286696Sian KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer")); 178283276Sgonzo 179286696Sian /* 180286696Sian * Setup eventtimer interrupt handling. Panic if anything goes wrong, 181286696Sian * because the system just isn't going to run without an eventtimer. 182286696Sian */ 183286696Sian sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, 184286696Sian &sc->tmr_irq_rid, RF_ACTIVE); 185286696Sian if (sc->tmr_irq_res == NULL) 186286696Sian panic("am335x_dmtimer: could not allocate irq resources"); 187283276Sgonzo if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK, 188286696Sian am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0) 189286696Sian panic("am335x_dmtimer: count not setup irq handler"); 190283276Sgonzo 191286696Sian sc->func.et.et_name = sc->tmr_name; 192283276Sgonzo sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; 193286696Sian sc->func.et.et_quality = 500; 194283276Sgonzo sc->func.et.et_frequency = sc->sysclk_freq; 195283276Sgonzo sc->func.et.et_min_period = 196283276Sgonzo ((0x00000005LLU << 32) / sc->func.et.et_frequency); 197283276Sgonzo sc->func.et.et_max_period = 198283276Sgonzo (0xfffffffeLLU << 32) / sc->func.et.et_frequency; 199286696Sian sc->func.et.et_start = am335x_dmtimer_et_start; 200286696Sian sc->func.et.et_stop = am335x_dmtimer_et_stop; 201283276Sgonzo sc->func.et.et_priv = sc; 202283276Sgonzo 203283276Sgonzo am335x_dmtimer_et_sc = sc; 204286696Sian et_register(&sc->func.et); 205283276Sgonzo 206283276Sgonzo return (0); 207283276Sgonzo} 208283276Sgonzo 209286696Sianstatic unsigned 210286696Sianam335x_dmtimer_tc_get_timecount(struct timecounter *tc) 211286696Sian{ 212286696Sian struct am335x_dmtimer_softc *sc; 213286696Sian 214286696Sian sc = tc->tc_priv; 215286696Sian 216286696Sian return (DMTIMER_READ4(sc, DMT_TCRR)); 217286696Sian} 218286696Sian 219283276Sgonzostatic int 220286696Sianam335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc) 221283276Sgonzo{ 222286696Sian KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter")); 223283276Sgonzo 224283276Sgonzo /* Set up timecounter, start it, register it. */ 225283276Sgonzo DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); 226283276Sgonzo while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) 227283276Sgonzo continue; 228283276Sgonzo 229283276Sgonzo sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; 230283276Sgonzo DMTIMER_WRITE4(sc, DMT_TLDR, 0); 231283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCRR, 0); 232283276Sgonzo DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); 233283276Sgonzo 234286696Sian sc->func.tc.tc_name = sc->tmr_name; 235283276Sgonzo sc->func.tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; 236283276Sgonzo sc->func.tc.tc_counter_mask = ~0u; 237283276Sgonzo sc->func.tc.tc_frequency = sc->sysclk_freq; 238286696Sian sc->func.tc.tc_quality = 500; 239283276Sgonzo sc->func.tc.tc_priv = sc; 240283276Sgonzo 241283276Sgonzo am335x_dmtimer_tc_sc = sc; 242286696Sian tc_init(&sc->func.tc); 243283276Sgonzo 244331907Sgonzo#ifdef MULTIDELAY 245331907Sgonzo arm_set_delay(am335x_dmtimer_delay, sc); 246331907Sgonzo#endif 247331907Sgonzo 248283276Sgonzo return (0); 249283276Sgonzo} 250283276Sgonzo 251283276Sgonzostatic int 252239281Sgonzoam335x_dmtimer_probe(device_t dev) 253239281Sgonzo{ 254286696Sian char strbuf[32]; 255286696Sian int tmr_num; 256239281Sgonzo 257261410Sian if (!ofw_bus_status_okay(dev)) 258261410Sian return (ENXIO); 259261410Sian 260286696Sian if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 261286696Sian return (ENXIO); 262239281Sgonzo 263286696Sian /* 264286696Sian * Get the hardware unit number (the N from ti,hwmods="timerN"). 265286696Sian * If this isn't the hardware unit we're going to use for either the 266286696Sian * eventtimer or the timecounter, no point in instantiating the device. 267286696Sian */ 268286696Sian tmr_num = ti_hwmods_get_unit(dev, "timer"); 269286696Sian if (tmr_num != ET_TMR_NUM && tmr_num != TC_TMR_NUM) 270286696Sian return (ENXIO); 271286696Sian 272286696Sian snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num); 273286696Sian device_set_desc_copy(dev, strbuf); 274286696Sian 275286696Sian return(BUS_PROBE_DEFAULT); 276239281Sgonzo} 277239281Sgonzo 278239281Sgonzostatic int 279239281Sgonzoam335x_dmtimer_attach(device_t dev) 280239281Sgonzo{ 281259744Sian struct am335x_dmtimer_softc *sc; 282286696Sian clk_ident_t timer_id; 283239281Sgonzo int err; 284239281Sgonzo 285259744Sian sc = device_get_softc(dev); 286283276Sgonzo sc->dev = dev; 287259744Sian 288259744Sian /* Get the base clock frequency. */ 289286696Sian if ((err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq)) != 0) 290286696Sian return (err); 291286696Sian 292286696Sian /* Enable clocks and power on the device. */ 293286696Sian if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT) 294239281Sgonzo return (ENXIO); 295286696Sian if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0) 296286696Sian return (err); 297286696Sian if ((err = ti_prcm_clk_enable(timer_id)) != 0) 298286696Sian return (err); 299239281Sgonzo 300259744Sian /* Request the memory resources. */ 301283276Sgonzo sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 302283276Sgonzo &sc->tmr_mem_rid, RF_ACTIVE); 303283276Sgonzo if (sc->tmr_mem_res == NULL) { 304239281Sgonzo return (ENXIO); 305239281Sgonzo } 306239281Sgonzo 307286696Sian sc->tmr_num = ti_hwmods_get_unit(dev, "timer"); 308286696Sian snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); 309239281Sgonzo 310286696Sian /* 311286696Sian * Go set up either a timecounter or eventtimer. We wouldn't have 312286696Sian * attached if we weren't one or the other. 313286696Sian */ 314286696Sian if (sc->tmr_num == ET_TMR_NUM) 315286696Sian am335x_dmtimer_et_init(sc); 316286696Sian else if (sc->tmr_num == TC_TMR_NUM) 317286696Sian am335x_dmtimer_tc_init(sc); 318286696Sian else 319286696Sian panic("am335x_dmtimer: bad timer number %d", sc->tmr_num); 320259750Sian 321239281Sgonzo return (0); 322239281Sgonzo} 323239281Sgonzo 324239281Sgonzostatic device_method_t am335x_dmtimer_methods[] = { 325239281Sgonzo DEVMETHOD(device_probe, am335x_dmtimer_probe), 326239281Sgonzo DEVMETHOD(device_attach, am335x_dmtimer_attach), 327239281Sgonzo { 0, 0 } 328239281Sgonzo}; 329239281Sgonzo 330239281Sgonzostatic driver_t am335x_dmtimer_driver = { 331239281Sgonzo "am335x_dmtimer", 332239281Sgonzo am335x_dmtimer_methods, 333239281Sgonzo sizeof(struct am335x_dmtimer_softc), 334239281Sgonzo}; 335239281Sgonzo 336239281Sgonzostatic devclass_t am335x_dmtimer_devclass; 337239281Sgonzo 338239281SgonzoDRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0); 339239281SgonzoMODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1); 340239281Sgonzo 341331907Sgonzostatic void 342331907Sgonzoam335x_dmtimer_delay(int usec, void *arg) 343239281Sgonzo{ 344331907Sgonzo struct am335x_dmtimer_softc *sc = arg; 345259744Sian int32_t counts; 346239281Sgonzo uint32_t first, last; 347239281Sgonzo 348239281Sgonzo /* Get the number of times to count */ 349259744Sian counts = (usec + 1) * (sc->sysclk_freq / 1000000); 350239281Sgonzo 351283276Sgonzo first = DMTIMER_READ4(sc, DMT_TCRR); 352239281Sgonzo 353239281Sgonzo while (counts > 0) { 354283276Sgonzo last = DMTIMER_READ4(sc, DMT_TCRR); 355259744Sian if (last > first) { 356239281Sgonzo counts -= (int32_t)(last - first); 357239281Sgonzo } else { 358239281Sgonzo counts -= (int32_t)((0xFFFFFFFF - first) + last); 359239281Sgonzo } 360239281Sgonzo first = last; 361239281Sgonzo } 362239281Sgonzo} 363239281Sgonzo 364331907Sgonzo#ifndef MULTIDELAY 365331907Sgonzovoid 366331907SgonzoDELAY(int usec) 367331907Sgonzo{ 368331907Sgonzo int32_t counts; 369331907Sgonzo 370331907Sgonzo if (am335x_dmtimer_tc_sc == NULL) { 371331907Sgonzo for (; usec > 0; usec--) 372331907Sgonzo for (counts = 200; counts > 0; counts--) 373331907Sgonzo /* Prevent gcc from optimizing out the loop */ 374331907Sgonzo cpufunc_nullop(); 375331907Sgonzo return; 376331907Sgonzo } else 377331907Sgonzo am335x_dmtimer_delay(usec, am335x_dmtimer_tc_sc); 378331907Sgonzo} 379331907Sgonzo#endif 380