Date: Wed, 20 Jun 2012 15:37:55 +0000 From: aleek@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r238005 - in soc2012/aleek/beaglexm-armv6/sys: arm/conf arm/ti/am37x boot/fdt/dts Message-ID: <20120620153755.0632B106564A@hub.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: aleek Date: Wed Jun 20 15:37:54 2012 New Revision: 238005 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=238005 Log: Major refactoring to gptimer driver Added: soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer_tc.c Modified: soc2012/aleek/beaglexm-armv6/sys/arm/conf/BEAGLEBOARD-XM soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.h soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/files.am37x soc2012/aleek/beaglexm-armv6/sys/boot/fdt/dts/beagleboardxm.dts Modified: soc2012/aleek/beaglexm-armv6/sys/arm/conf/BEAGLEBOARD-XM ============================================================================== --- soc2012/aleek/beaglexm-armv6/sys/arm/conf/BEAGLEBOARD-XM Wed Jun 20 14:47:39 2012 (r238004) +++ soc2012/aleek/beaglexm-armv6/sys/arm/conf/BEAGLEBOARD-XM Wed Jun 20 15:37:54 2012 (r238005) @@ -58,7 +58,7 @@ options WITNESS #Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed #options DIAGNOSTIC -options DEBUG +#options DEBUG # NFS support #options NFSCL Modified: soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c ============================================================================== --- soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c Wed Jun 20 14:47:39 2012 (r238004) +++ soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c Wed Jun 20 15:37:54 2012 (r238005) @@ -798,10 +798,7 @@ static int omap3_gptimer_probe(device_t dev) { - struct omap3_gptimer_softc *sc; - sc = (struct omap3_gptimer_softc *)device_get_softc(dev); - device_printf(dev, "PROBING...\n"); - printf("gptimer: PROBING...\n"); + //struct omap3_gptimer_softc *sc = (struct omap3_gptimer_softc *)device_get_softc(dev); if (ofw_bus_is_compatible(dev, "ti,omap3_gptimer")) { device_set_desc(dev, "OMAP3 General Purpose Timer"); @@ -951,6 +948,6 @@ }; static devclass_t g_omap3_gptimer_devclass; -DRIVER_MODULE(omap3_gptimer, omap, g_omap3_gptimer_driver, g_omap3_gptimer_devclass, 0, 0); -MODULE_DEPEND(omap3_gptimer, ti_prcm, 1, 1, 1); +DRIVER_MODULE(omap3_gptimer, simplebus, g_omap3_gptimer_driver, g_omap3_gptimer_devclass, 0, 0); +///MODULE_DEPEND(omap3_gptimer, ti_prcm, 1, 1, 1); Modified: soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.h ============================================================================== --- soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.h Wed Jun 20 14:47:39 2012 (r238004) +++ soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.h Wed Jun 20 15:37:54 2012 (r238005) @@ -74,11 +74,13 @@ #define OMAP3_GPTIMER_ASSERT_LOCKED(_tmr) mtx_assert(&(_tmr)->mtx, MA_OWNED); #define OMAP3_GPTIMER_ASSERT_UNLOCKED(_tmr) mtx_assert(&(_tmr)->mtx, MA_NOTOWNED); -/* - * Data structure per Timer. +/** + * Timer driver context, allocated and stored globally, this driver is not + * intended to ever be unloaded (see g_omap3_gptimer_sc). + * */ -struct omap3_gptimer { - +struct omap3_gptimer_softc { + device_t sc_dev; /* Flags indicating current and configured status */ unsigned int flags; @@ -90,7 +92,7 @@ /* The memory resource for the timer register set */ struct resource* mem_res; - + ; /* The IRQ resource and handle (if installed) for the timer */ struct resource* irq_res; void* irq_h; @@ -106,60 +108,47 @@ /* The source clock to use */ unsigned int source; -}; - - -/** - * Timer driver context, allocated and stored globally, this driver is not - * intended to ever be unloaded (see g_omap3_gptimer_sc). - * - */ -struct omap3_gptimer_softc { - device_t sc_dev; - unsigned int sc_num_timers; - struct omap3_gptimer sc_timers[OMAP3_NUM_TIMERS]; - struct omap3_gptimer *sc_tick_timer; }; void -omap3_gptimer_intr_filter_ack(unsigned int n); +omap3_gptimer_intr_filter_ack(struct omap3_gptimer_softc *sc); static unsigned omap3_gptimer_tc_get_timecount(struct timecounter *tc); static inline uint32_t -omap3_gptimer_readl(struct omap3_gptimer *timer, bus_size_t off); +omap3_gptimer_readl(struct omap3_gptimer_softc *sc, bus_size_t off); static inline void -omap3_gptimer_writel(struct omap3_gptimer *timer, bus_size_t off, uint32_t val); +omap3_gptimer_writel(struct omap3_gptimer_softc *sc, bus_size_t off, uint32_t val); int -omap3_gptimer_activate(unsigned int n, unsigned int flags, unsigned int time_us, +omap3_gptimer_activate(struct omap3_gptimer_softc *sc, unsigned int flags, unsigned int time_us, void (*callback)(void *data), void *data); int -omap3_gptimer_start(unsigned int n); +omap3_gptimer_start(struct omap3_gptimer_softc *sc); int -omap3_gptimer_stop(unsigned int n); +omap3_gptimer_stop(struct omap3_gptimer_softc *sc); int -omap3_gptimer_get_freq(unsigned int n, uint32_t *freq); +omap3_gptimer_get_freq(struct omap3_gptimer_softc *sc, uint32_t *freq); static int omap3_calibrate_delay_loop(struct timecounter *tc); int -omap3_gptimer_set_intr_filter(unsigned int n, driver_filter_t filter); +omap3_gptimer_set_intr_filter(struct omap3_gptimer_softc *sc, driver_filter_t filter); static void omap3_gptimer_intr(void *arg); - +#if 0 static int omap3_timer_tick_intr(void *arg); - +#endif static int omap3_gptimer_probe(device_t dev); @@ -167,7 +156,8 @@ omap3_gptimer_attach(device_t dev); int -omap3_gptimer_write_count(unsigned int n, uint32_t cnt); +omap3_gptimer_write_count(struct omap3_gptimer_softc *sc, uint32_t cnt); int -omap3_gptimer_read_count(unsigned int n, uint32_t *cnt); +omap3_gptimer_read_count(struct omap3_gptimer_softc *sc, uint32_t *cnt); + Added: soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer_tc.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer_tc.c Wed Jun 20 15:37:54 2012 (r238005) @@ -0,0 +1,863 @@ + +/*- + * Copyright (c) 2012 Aleksander Dutkowski <aleek@Freebsd.org> + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on gptimer driver by Ben Gray + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/time.h> +#include <sys/bus.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/timetc.h> +#include <sys/lock.h> +#include <sys/gpio.h> +#include <sys/mutex.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/cpufunc.h> +#include <machine/frame.h> +#include <machine/resource.h> +#include <machine/intr.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <arm/ti/ti_cpuid.h> +#include <arm/ti/ti_prcm.h> +#include <arm/ti/am37x/am37x_gptimer.h> + +static struct omap3_gptimer_softc *g_omap3_gptimer_sc = NULL; + +static unsigned int delay_loops_per_us = 100; + + +/** + * Timer for tick counting. This is used to measure time by CPU + */ +//static struct omap3_gptimer *omap3_gptimer_tc_tmr = NULL; + +/** + * Struct used by tc_init(), to init the timecounter + */ +static struct timecounter omap3_gptimer_tc = { + /* Name of the timecounter. */ + .tc_name = "OMAP3 Timecouter", + /* + * This function reads the counter. It is not required to + * mask any unimplemented bits out, as long as they are + * constant. + */ + .tc_get_timecount = omap3_gptimer_tc_get_timecount, + .tc_poll_pps = NULL, + /* This mask should mask off any unimplemented bits. */ + .tc_counter_mask = ~0u, + /* Frequency of the counter in Hz. */ + .tc_frequency = 0, + /* + * Used to determine if this timecounter is better than + * another timecounter higher means better. Negative + * means "only use at explicit request". + */ + .tc_quality = 1000, +}; + +#define __omap3_delay(i) \ + do { \ + unsigned int cnt = (i); \ + __asm __volatile("1: subs %0, %0, 1\n" \ + " bne 1b\n" \ + : "+r" (cnt) : : "cc"); \ + } while(0) + +/** + * omap3_calibrate_delay_loop - uses the setup timecounter to configure delay + * + * This is not very scientfic, basically just use the timecount to measure the + * time to do 1000 delay loops (for loop with 1024 loops). + * + * + */ +static int +omap3_calibrate_delay_loop(struct timecounter *tc) +{ + u_int oldirqstate; + unsigned int start, end; + uint64_t nanosecs; + + /* Disable interrupts to ensure they don't mess up the calculation */ + oldirqstate = disable_interrupts(I32_bit); + + start = omap3_gptimer_tc_get_timecount(tc); + //__omap3_delay(10240); + for (int usec=10240; usec > 0; usec--) + for (int32_t counts = 200; counts > 0; counts--) + /* Prevent gcc from optimizing out the loop */ + cpufunc_nullop(); + + end = omap3_gptimer_tc_get_timecount(tc); + + restore_interrupts(oldirqstate); + + /* Calculate the number of loops in 1us */ + nanosecs = ((uint64_t)(end - start) * 1000000000ULL) / tc->tc_frequency; + delay_loops_per_us = (unsigned int)((uint64_t)(10240 * 1000) / nanosecs); + + printf("OMAP3: delay loop calibrated to %u cycles\n", delay_loops_per_us); + + return (0); +} + + +static unsigned +omap3_gptimer_tc_get_timecount(struct timecounter *tc) +{ + uint32_t count; + omap3_gptimer_read_count(g_omap3_gptimer_sc, &count); + return(count); + +} + +/** + * omap3_gptimer_read_count - reads the current timer value + * @n: the number of the timer (first timer is number 1) + * @cnt: + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_read_count(struct omap3_gptimer_softc *sc, uint32_t *cnt) +{ + int ret; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + + /* Get a pointer to the individual sc struct */ + + OMAP3_GPTIMER_LOCK(sc); + + if (!(sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG)) { + ret = EINVAL; + } else { + *cnt = omap3_gptimer_readl(sc, OMAP3_GPT_TCRR); + ret = 0; + } + + OMAP3_GPTIMER_UNLOCK(sc); + + return (ret); +} + + +/** + * omap3_gptimer_write_count - writes a value into the current count + * @n: the number of the timer (first timer is number 1) + * @cnt: the value to put in the count register + * + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_write_count(struct omap3_gptimer_softc *sc, uint32_t cnt) +{ + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + + if (!(sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP3_GPTIMER_LOCK(sc); + + omap3_gptimer_writel(sc, OMAP3_GPT_TCRR, cnt); + + OMAP3_GPTIMER_UNLOCK(sc); + + return (0); +} + + +/** + * omap3_gptimer_readl - reads a 32-bit value from one of the timer registers + * @timer: Timer device context + * @off: The offset of a register from the timer register address range + * + * + * RETURNS: + * 32-bit value read from the register. + */ +static inline uint32_t +omap3_gptimer_readl(struct omap3_gptimer_softc *sc, bus_size_t off) +{ + return (bus_read_4(sc->mem_res, off)); +} + +/** + * omap3_gptimer_writel - writes a 32-bit value to one of the timer registers + * @timer: Timer device context + * @off: The offset of a register from the timer register address range + * @val: The value to write into the register + * + * + * RETURNS: + * nothing + */ +static inline void +omap3_gptimer_writel(struct omap3_gptimer_softc *sc, bus_size_t off, uint32_t val) +{ + bus_write_4(sc->mem_res, off, val); +} + +/** + * omap_gptimer_get_freq - gets the frequency of an activated timer + * @n: the number of the timer (first timer is number 1) + * @freq: unpon return will contain the current freq + * + * The timer must be activated, if not this function will return EINVAL. + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_get_freq(struct omap3_gptimer_softc *sc, uint32_t *freq) +{ + unsigned int src_freq; + unsigned int tmr_freq; + unsigned int prescaler; + uint32_t tclr, tldr; + int rc; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if (freq == NULL) + return (EINVAL); + + /* Get a pointer to the individual timer struct */ + if (!(sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + /* We get the frequency by first reading the source frequency */ + if ((rc = ti_prcm_clk_get_source_freq(sc->source, &src_freq)) != 0) + return (rc); + + + OMAP3_GPTIMER_LOCK(sc); + + /* Determine if the pre-scalar is enabled and if so the prescaler value */ + tclr = omap3_gptimer_readl(sc, OMAP3_GPT_TCLR); + if (tclr & TCLR_PRE) + prescaler = 1UL << (((tclr & TCLR_PTV_MASK) >> 2) + 1); + else + prescaler = 1; + + /* Get the reload count */ + tldr = omap3_gptimer_readl(sc, OMAP3_GPT_TLDR); + + OMAP3_GPTIMER_UNLOCK(sc); + + + /* Calculate the tick freq */ + tmr_freq = (src_freq / prescaler); + + /* If auto-reload mode is set and the reload count is not zero then the + * frequency is the period between overflows. + */ + if ((tclr & TCLR_AR) && (tldr != 0x00000000)) { + tmr_freq /= ((0xffffffff - tldr) + 1); + } + + + if (freq != NULL) + *freq = tmr_freq; + + return (0); +} + + + +/** + * omap3_gptimer_activate - configures the timer + * @n: the number of the timer (first timer is number 1) + * @flags: defines the type of timer to turn on + * @time_ns: the period of the timer in nanoseconds + * @callback: if defined this function will be called when the timer overflows + * @data: data value to pass to the callback + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_activate(struct omap3_gptimer_softc *sc, unsigned int flags, unsigned int time_us, + void (*callback)(void *data), void *data) +{ + uint32_t val; + uint64_t tickcount; + unsigned int freq; + uint64_t freq64; + uint32_t prescaler; + uint32_t startcount; + + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + + /* Sanity check the timer is availabe and not activated */ + if (!(sc->flags & OMAP3_GPTIMER_AVAILABLE_FLAG)) { + device_printf(sc->sc_dev, "Error: timer not available\n"); + return (EINVAL); + } + if (sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG) { + device_printf(sc->sc_dev, "Error: timer already activated\n"); + return (EINVAL); + } + + /* Set up system clock information */ + if (ti_prcm_clk_valid(sc->source) != 0) { + device_printf(sc->sc_dev, "Error: failed to find source clock\n"); + return (EINVAL); + } + + OMAP3_GPTIMER_LOCK(sc); + + /* Enable the functional and interface clock */ + if (flags & OMAP3_GPTIMER_32KCLK_FLAG) + ti_prcm_clk_set_source(sc->source, F32KHZ_CLK); + else + ti_prcm_clk_set_source(sc->source, SYSCLK_CLK); + + ti_prcm_clk_enable(sc->source); + + + /* Store the flags in the timer context */ + sc->flags &= 0xFF000000; + sc->flags |= (0x00FFFFFF & flags); + + + /* Reset the timer and poll on the reset complete flag */ + if (sc->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + omap3_gptimer_writel(sc, OMAP3_GPT_TIOCP_CFG, 0x2); + /* TODO: add a timeout */ + while ((omap3_gptimer_readl(sc, OMAP3_GPT_TISTAT) & 0x01) == 0x00) + continue; + + /* Clear the interrupt status */ + omap3_gptimer_writel(sc, OMAP3_GPT_TISR, TCAR | OVF | MAT); + } + + /* If the user supplied a zero value we set a free running timer */ + if (time_us == 0) { + /* Set the initial value and roll over value to 0 */ + startcount = 0x00000000; + } else { + /* We need to calculate the number of timer ticks in either the reload + * value (for periodic timers) or the overflow + */ + ti_prcm_clk_get_source_freq(sc->source, &freq); + freq64 = freq; + + /* Calculate the period of the timer, 64 bit calculations used to + * prevent rollover. + */ + tickcount = (freq64 * (uint64_t)time_us) / 1000000; + + /* Must have a count of at least 1 */ + if (tickcount == 0) + tickcount = 1; + + /* If the number is too large then see if by enabling the prescaler it + * will fit, otherwise just set the max count we can do. + */ + if (tickcount > 0xFFFFFFFFULL) { + + /* Try and find a prescaler that will match */ + for (prescaler = 0; prescaler < 7; prescaler++) { + if (tickcount < (0x1ULL << (32 + prescaler))) { + break; + } + } + + /* Adjust the count and apply the prescaler */ + tickcount >>= (prescaler + 1); + + val = omap3_gptimer_readl(sc, OMAP3_GPT_TCLR); + val &= ~TCLR_PTV_MASK; + val |= TCLR_PRE | (prescaler << 2); + omap3_gptimer_writel(sc, OMAP3_GPT_TCLR, val); + } + + /* Calculate the start value */ + startcount = 0xFFFFFFFFUL - (uint32_t)(tickcount & 0xFFFFFFFFUL); + + printf("[BRG] %s, %d : freq64=%llu : tickcount=%llu : startcount=%u : time_us=%u\n", + __func__, __LINE__, freq64, tickcount, startcount, time_us); + } + + /* Load the start value into the count register */ + omap3_gptimer_writel(sc, OMAP3_GPT_TCRR, startcount); + + /* Enable autoload mode if configuring a periodic timer or system tick + * timer. Also set the reload count to match the period tick count. + */ + if (flags & OMAP3_GPTIMER_PERIODIC_FLAG) { + /* Enable auto reload */ + val = omap3_gptimer_readl(sc, OMAP3_GPT_TCLR); + val |= TCLR_AR; + omap3_gptimer_writel(sc, OMAP3_GPT_TCLR, val); + + /* Set the reload value */ + omap3_gptimer_writel(sc, OMAP3_GPT_TLDR, startcount); + } + + /* If a callback function has been supplied setup a overflow interrupt */ + if (callback != NULL) { + + /* Save the callback function and the data for it */ + sc->callback = callback; + sc->callback_data = data; + + /* Activate the interrupt */ + if (bus_setup_intr(sc->sc_dev, sc->irq_res, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, omap3_gptimer_intr, + (void*)sc, &sc->irq_h)) { + device_printf(sc->sc_dev, "Error: failed to activate interrupt\n"); + } + + /* Enable the overflow interrupts. */ + if (sc->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + val = omap3_gptimer_readl(sc, OMAP3_GPT_TIER); + val |= OVF; + omap3_gptimer_writel(sc, OMAP3_GPT_TIER, val); + } + } + + /* Finally set the activated flag */ + sc->flags |= OMAP3_GPTIMER_ACTIVATED_FLAG; + + OMAP3_GPTIMER_UNLOCK(sc); + + printf("[BRG] %s, %d\n", __func__, __LINE__); + + return (0); +} + +/** + * omap3_gptimer_start - starts a one-shot or periodic timer + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_start(struct omap3_gptimer_softc *sc) +{ + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if (!(sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP3_GPTIMER_LOCK(sc); + + val = omap3_gptimer_readl(sc, OMAP3_GPT_TCLR); + val |= TCLR_ST; + omap3_gptimer_writel(sc, OMAP3_GPT_TCLR, val); + + OMAP3_GPTIMER_UNLOCK(sc); + + return 0; +} + +/** + * omap3_gptimer_stop - stops a one-shot or periodic timer + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_stop(struct omap3_gptimer_softc *sc) +{ + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + if (!(sc->flags & OMAP3_GPTIMER_ACTIVATED_FLAG)) + return (EINVAL); + + OMAP3_GPTIMER_LOCK(sc); + + val = omap3_gptimer_readl(sc, OMAP3_GPT_TCLR); + val &= ~TCLR_ST; + omap3_gptimer_writel(sc, OMAP3_GPT_TCLR, val); + + OMAP3_GPTIMER_UNLOCK(sc); + + return 0; +} + +/** + * omap3_gptimer_set_intr_filter - sets a filter + * @n: the number of the timer (first timer is number 1) + * + * + * RETURNS: + * Returns 0 on success, otherwise an error code + */ +int +omap3_gptimer_set_intr_filter(struct omap3_gptimer_softc *sc, driver_filter_t filter) +{ + uint32_t val; + + /* Sanity checks */ + if (sc == NULL) + return (ENOMEM); + + OMAP3_GPTIMER_LOCK(sc); + + /* If a callback is already installed this won't work */ + if (sc->callback != NULL) { + OMAP3_GPTIMER_UNLOCK(sc); + return(EINVAL); + } + + /* Sanity check the timer is already activated and periodic type */ + if ((sc->flags & (OMAP3_GPTIMER_ACTIVATED_FLAG | OMAP3_GPTIMER_PERIODIC_FLAG)) + != (OMAP3_GPTIMER_ACTIVATED_FLAG | OMAP3_GPTIMER_PERIODIC_FLAG)) { + OMAP3_GPTIMER_UNLOCK(sc); + return(EINVAL); + } + + + /* Attempt to activate the interrupt for the tick */ + if (bus_setup_intr(sc->sc_dev, sc->irq_res, INTR_TYPE_CLK, + filter, NULL, NULL, &sc->irq_h)) { + device_printf(sc->sc_dev, "Error: failed to activate interrupt\n"); + OMAP3_GPTIMER_UNLOCK(sc); + return(EINVAL); + } + + + /* Enable the overflow interrupts */ + if (sc->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + val = omap3_gptimer_readl(sc, OMAP3_GPT_TIER); + val |= OVF; + omap3_gptimer_writel(sc, OMAP3_GPT_TIER, val); + } + + OMAP3_GPTIMER_UNLOCK(sc); + + return(0); +} + +/** + * omap3_gptimer_intr_filter_ack - acknowledges a timer interrupt + * @n: the number of the timer (first timer is number 1) + * + * This function should only be called from filter interrupt handler installed + * by calling the omap_gptimer_set_intr_filter() function. + * + * RETURNS: + * Nothing + */ +void +omap3_gptimer_intr_filter_ack(struct omap3_gptimer_softc *sc) +{ + uint32_t stat; + + OMAP3_GPTIMER_LOCK(sc); + + /* Read the interrupt status flag and clear it */ + /* Read the status and it with the enable flag */ + stat = omap3_gptimer_readl(sc, OMAP3_GPT_TISR); + stat &= omap3_gptimer_readl(sc, OMAP3_GPT_TIER); + + /* Clear the status flag */ + omap3_gptimer_writel(sc, OMAP3_GPT_TISR, stat); + OMAP3_GPTIMER_UNLOCK(sc); +} + + + +/** + * cpu_initclocks - function called by the system in init the tick clock/timer + * + * This is where both the timercount and system ticks timer are started. + * + * RETURNS: + * nothing + */ +void +cpu_initclocks(void) +{ + cpu_initclocks_bsp(); +} + +/** + * DELAY - Delay for at least N microseconds. + * @n: number of microseconds to delay by + * + * This function is called all over the kernel and is suppose to provide a + * consistent delay. It is a busy loop and blocks polling a timer when called. + * + * RETURNS: + * nothing + */ +void +DELAY(int usec) +{ + int32_t counts; + //uint32_t first, last; + + if (g_omap3_gptimer_sc == NULL) { + for (; usec > 0; usec--) + for (counts = 200; counts > 0; counts--) + /* Prevent gcc from optimizing out the loop */ + cpufunc_nullop(); + return; + } +} + +static void +omap3_gptimer_intr(void *arg) +{ + struct omap3_gptimer_softc *sc = (struct omap3_gptimer_softc *)arg; + void (*callback)(void *data); + void* callback_data; + uint32_t stat = 0x0000; + + OMAP3_GPTIMER_LOCK(sc); + + /* Read the interrupt status flag and clear it */ + if (sc->profile == OMAP_GPTIMER_PROFILE_OMAP3) { + + /* Read the status and it with the enable flag */ + stat = omap3_gptimer_readl(sc, OMAP3_GPT_TISR); + stat &= omap3_gptimer_readl(sc, OMAP3_GPT_TIER); + + /* Clear the status flag */ + omap3_gptimer_writel(sc, OMAP3_GPT_TISR, stat); + } + + /* Store the callback details before releasing the lock */ + callback = sc->callback; + callback_data = sc->callback_data; + + OMAP3_GPTIMER_UNLOCK(sc); + + /* Check if an actual overflow interrupt */ + if ((stat & OVF) && (callback != NULL)) + callback(sc->callback_data); +} + +/** + * omap3_clk_intr - interrupt handler for the tick timer (GPTIMER10) + * @arg: the trapframe, needed for the hardclock system function. + * + * This interrupt is triggered every hz times a second. It's role is basically + * to just clear the interrupt status and set it up for triggering again, plus + * tell the system a tick timer has gone off by calling the hardclock() + * function from the kernel API. + * + * RETURNS: + * Always returns FILTER_HANDLED. + */ +#if 0 +static int +omap3_timer_tick_intr(void *arg) +{ + struct trapframe *frame = arg; +#if defined(OMAP3_HEARTBEAT_GPIO) + static int heartbeat_cnt = 0; +#endif + + /* Acknowledge the interrupt */ + omap3_gptimer_intr_filter_ack(TICKTIMER_GPTIMER); + + /* Heartbeat */ +#if defined(OMAP3_HEARTBEAT_GPIO) + if (heartbeat_cnt++ >= (hz/2)) { + //printf("[BRG] ** tick **\n"); + gpio_pin_toggle(OMAP3_HEARTBEAT_GPIO); + heartbeat_cnt = 0; + } +#endif + + /* Do what we came here for */ + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + + /* Indicate we've handed the interrupt */ + return (FILTER_HANDLED); +} +#endif + + +static int +omap3_gptimer_probe(device_t dev) +{ + //struct omap3_gptimer_softc *sc = (struct omap3_gptimer_softc *)device_get_softc(dev); + + if (ofw_bus_is_compatible(dev, "ti,omap3_gptimer_tc")) { + device_set_desc(dev, "OMAP3 General Purpose Timer - Tick Counter"); + return(BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +omap3_gptimer_attach(device_t dev) +{ + struct omap3_gptimer_softc *sc = device_get_softc(dev); + char name[32]; + int rid=0; // resource id for device, unique + uint32_t rev; + u_int oldirqstate; + unsigned int timer_freq; + + device_printf( dev, "Attaching the device..." ); + + + // Setup the basics + //sc->sc_dev = dev; + //sc->sc_tick_timer = NULL; + + /* First try and get the register addresses, if this fails we assume + * there are no more timers. + */ + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if(sc->mem_res == NULL) + { + device_printf(dev, "could not allocate resources\n" ); + return (ENXIO); + } + + /* Next try and get the interrupt resource, this is not fatal */ + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); //@TODO XXX Why shareable? + + /* Mutex to protect the shared data structures */ + snprintf(name, 32, "omap_gptimer_tc"); + mtx_init(&sc->mtx, device_get_nameunit(dev), name, MTX_SPIN); + + // I decided to delete support for OMAP4 timers from the original code - aleek + rev = omap3_gptimer_readl(sc, OMAP3_GPT_TIDR); + switch (rev) { + case 0x00000013: /* OMAP3 without 1ms generation */ + case 0x00000015: + case 0x00000021: /* OMAP3 with 1ms generation */ + sc->profile = OMAP_GPTIMER_PROFILE_OMAP3; + break; + break; + default: + sc->profile = OMAP_GPTIMER_PROFILE_UNKNOWN; + break; + } + + if (sc->profile == OMAP_GPTIMER_PROFILE_UNKNOWN) { + device_printf(dev, "Error: failed to determine the type of " + "GPTIMER_tc, ignoring timer (rev: 0x%08x)\n", + rev); + return (ENXIO); + } + /* Set the clock source for the timer, this is just a one to one + * mapping of the clock id to timer, i.e. n=0 => GPTIMER1_CLK. + */ + sc->source = GPTIMER10_CLK; // @TODO XXX fix this - the timer number shouldn't be hardcoded + + /* Finally mark the timer as available */ + sc->flags = OMAP3_GPTIMER_AVAILABLE_FLAG; + + /* setup GPTIMER10 for system ticks, and GPTIMER11 for general purpose counter */ + oldirqstate = disable_interrupts(I32_bit); + + /* Setup another timer to be the timecounter */ + if (omap3_gptimer_activate(sc, OMAP3_GPTIMER_PERIODIC_FLAG, 0, NULL, NULL)) { + device_printf(dev, "Error: failed to activate system tick timer\n"); + } else if (omap3_gptimer_start(sc)) { + device_printf(dev, "Error: failed to start system tick timer\n"); + } + + /* Save the system clock speed */ + omap3_gptimer_get_freq(sc, &timer_freq); + omap3_gptimer_tc.tc_frequency = timer_freq; + + /* Setup the time counter */ + tc_init(&omap3_gptimer_tc); + + /* Calibrate the delay loop */ + omap3_calibrate_delay_loop(&omap3_gptimer_tc); + + /* Restore interrupt state */ + restore_interrupts(oldirqstate); + + g_omap3_gptimer_sc = sc; + + return 0; +} + +static device_method_t g_omap3_gptimer_methods[] = { + DEVMETHOD(device_probe, omap3_gptimer_probe), + DEVMETHOD(device_attach, omap3_gptimer_attach), + {0, 0}, +}; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120620153755.0632B106564A>