socsvn commit: r237543 - in
soc2012/aleek/beaglexm-armv6/sys/arm/ti: . am37x
aleek at FreeBSD.org
aleek at FreeBSD.org
Tue Jun 12 12:24:58 UTC 2012
Author: aleek
Date: Tue Jun 12 12:24:55 2012
New Revision: 237543
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=237543
Log:
Added code for OMAP3 General Purpose Timer - its not working for now
Added:
soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c
Modified:
soc2012/aleek/beaglexm-armv6/sys/arm/ti/ti_prcm.c
Added: soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ soc2012/aleek/beaglexm-armv6/sys/arm/ti/am37x/am37x_gptimer.c Tue Jun 12 12:24:55 2012 (r237543)
@@ -0,0 +1,538 @@
+
+/*-
+ * Copyright (c) 2012 Aleksander Dutkowski <aleek at 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
+ */
+
+#define OMAP3_NUM_TIMERS 12
+
+/*
+ * Ids of the timers used by the kernel
+ */
+#define TICKTIMER_GPTIMER 10
+#define TIMECOUNT_GPTIMER 11
+
+/*
+ * Standard registers for OMAP3 General Purpose Timers
+ */
+#define OMAP3_GPT_TIDR 0x0000
+#define OMAP3_GPT_TIOCP_CFG 0x0010
+#define OMAP3_GPT_TISTAT 0x0014
+#define OMAP3_GPT_TISR 0x0018
+#define OMAP3_GPT_TIER 0x001C
+#define OMAP3_GPT_TWER 0x0020
+#define OMAP3_GPT_TCLR 0x0024
+#define OMAP3_GPT_TCRR 0x0028
+#define OMAP3_GPT_TLDR 0x002C
+#define OMAP3_GPT_TTGR 0x0030
+#define OMAP3_GPT_TWPS 0x0034
+#define OMAP3_GPT_TMAR 0x0038
+#define OMAP3_GPT_TCAR1 0x003C
+#define OMAP3_GPT_TSICR 0x0040
+#define OMAP3_GPT_TCAR2 0x0044
+#define OMAP3_GPT_TPIR 0x0048
+#define OMAP3_GPT_TNIR 0x004C
+#define OMAP3_GPT_TCVR 0x0050
+#define OMAP3_GPT_TOCR 0x0054
+#define OMAP3_GPT_TOWR 0x0058
+
+/**
+ * Data structure per Timer.
+ */
+struct omap3_gptimer {
+
+ /* Flags indicating current and configured status */
+ unsigned int flags;
+
+#define OMAP_GPTIMER_AVAILABLE_FLAG 0x01000000
+#define OMAP_GPTIMER_ACTIVATED_FLAG 0x02000000
+
+ /* Lock taken when configuring the registers */
+ struct mtx mtx;
+
+ /* 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;
+
+ /* Callback function used when an interrupt is tripped on the given channel */
+ void (*callback)(void *data);
+
+ /* Callback data passed in the callback ... duh */
+ void* callback_data;
+
+ /* The profile of the timer, basically defines the register layout */
+ unsigned int profile;
+
+ /* 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_omap_gptimer_sc).
+ *
+ */
+struct omap3_gptimer_softc {
+ device_t sc_dev;
+ unsigned int sc_num_timers;
+ struct omap3_gptimer sc_timers[MAX_NUM_TIMERS];
+ struct omap3_gptimer *sc_tick_timer;
+
+};
+
+static struct omap3_gptimer_softc *g_omap3_gptimer_sc = NULL;
+
+
+/**
+ * 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,
+};
+
+static unsigned
+omap3_gptimer_tc_get_timecount(struct timecounter *tc)
+{
+ return omap3_gptimer_tc_read_4(DMTIMER_TCRR);
+}
+
+/**
+ * omap_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
+omap_gptimer_activate(unsigned int n, unsigned int flags, unsigned int time_us,
+ void (*callback)(void *data), void *data)
+{
+ struct omap_gptimer_softc *sc = g_omap3_gptimer_sc;
+ struct omap_gptimer *timer;
+ 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);
+ if ((n == 0) || (n > sc->sc_num_timers))
+ return (EINVAL);
+
+ /* Get a pointer to the individual timer struct */
+ timer = &sc->sc_timers[n-1];
+
+ /* Sanity check the timer is availabe and not activated */
+ if (!(timer->flags & OMAP_GPTIMER_AVAILABLE_FLAG)) {
+ device_printf(sc->sc_dev, "Error: timer %d not available\n", n);
+ return (EINVAL);
+ }
+ if (timer->flags & OMAP_GPTIMER_ACTIVATED_FLAG) {
+ device_printf(sc->sc_dev, "Error: timer %d already activated\n", n);
+ return (EINVAL);
+ }
+
+ /* Set up system clock information */
+ if (ti_prcm_clk_valid(timer->source) != 0) {
+ device_printf(sc->sc_dev, "Error: failed to find source clock\n");
+ return (EINVAL);
+ }
+
+
+ OMAP_GPTIMER_LOCK(timer);
+
+ /* Enable the functional and interface clock */
+ if (flags & OMAP_GPTIMER_32KCLK_FLAG)
+ omap_prcm_clk_set_source(timer->source, F32KHZ_CLK);
+ else
+ omap_prcm_clk_set_source(timer->source, SYSCLK_CLK);
+
+ omap_prcm_clk_enable(timer->source);
+
+
+ /* Store the flags in the timer context */
+ timer->flags &= 0xFF000000;
+ timer->flags |= (0x00FFFFFF & flags);
+
+
+ /* Reset the timer and poll on the reset complete flag */
+ if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) {
+ omap_gptimer_writel(timer, OMAP_GPT_TIOCP_CFG, 0x2);
+ /* TODO: add a timeout */
+ while ((omap_gptimer_readl(timer, OMAP_GPT_TISTAT) & 0x01) == 0x00)
+ continue;
+
+ /* Clear the interrupt status */
+ omap_gptimer_writel(timer, OMAP_GPT_TISR, TCAR | OVF | MAT);
+ }
+
+ else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) {
+ omap_gptimer_writel(timer, OMAP_GPT_TIOCP_CFG, 0x1);
+ /* TODO: add a timeout */
+ while ((omap_gptimer_readl(timer, OMAP_GPT_TIOCP_CFG) & 0x01) == 0x01)
+ continue;
+
+ /* Clear the interrupt status */
+ omap_gptimer_writel(timer, OMAP4_GPT_IRQSTATUS, 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
+ */
+ omap_prcm_clk_get_source_freq(timer->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 = omap_gptimer_readl(timer, timer->tclr);
+ val &= ~TCLR_PTV_MASK;
+ val |= TCLR_PRE | (prescaler << 2);
+ omap_gptimer_writel(timer, timer->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 */
+ omap_gptimer_writel(timer, timer->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 & OMAP_GPTIMER_PERIODIC_FLAG) {
+ /* Enable auto reload */
+ val = omap_gptimer_readl(timer, timer->tclr);
+ val |= TCLR_AR;
+ omap_gptimer_writel(timer, timer->tclr, val);
+
+ /* Set the reload value */
+ omap_gptimer_writel(timer, timer->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 */
+ timer->callback = callback;
+ timer->callback_data = data;
+
+ /* Activate the interrupt */
+ if (bus_setup_intr(sc->sc_dev, timer->irq_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, omap_gptimer_intr,
+ (void*)timer, &timer->irq_h)) {
+ device_printf(sc->sc_dev, "Error: failed to activate interrupt\n");
+ }
+
+ /* Enable the overflow interrupts. */
+ if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP3) {
+ val = omap_gptimer_readl(timer, OMAP_GPT_TIER);
+ val |= OVF;
+ omap_gptimer_writel(timer, OMAP_GPT_TIER, val);
+ }
+ else if (timer->profile == OMAP_GPTIMER_PROFILE_OMAP4) {
+ omap_gptimer_writel(timer, OMAP4_GPT_IRQENABLE_SET, OVF);
+ }
+ }
+
+
+ /* Finally set the activated flag */
+ timer->flags |= OMAP_GPTIMER_ACTIVATED_FLAG;
+
+ OMAP_GPTIMER_UNLOCK(timer);
+
+printf("[BRG] %s, %d\n", __func__, __LINE__);
+
+ return (0);
+}
+
+
+
+/**
+ * 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 (omap3_gptimer_tc_tmr == 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)
+{
+
+}
+
+static int
+omap3_gptimer_probe(device_t dev)
+{
+ device_set_desc(dev, "TI OMAP3 General Purpose Timers");
+ return (0);
+}
+
+static int
+omap3_gptimer_attach(device_t dev)
+{
+ struct omap3_gptimer_softc *sc = device_get_softc(dev);
+ struct omap3_gptimer *timer;
+ char name[32];
+ unsigned int n = 0;
+ int rid; // resource id for device, unique
+ uint32_t rev;
+ u_int oldirqstate;
+ unsigned int timer_freq;
+
+
+ // Setup the basics
+ sc->sc_dev = dev;
+ sc->sc_tick_timer = NULL;
+
+ // Go through and configure each individual timer */
+ for (n = 0; n < OMAP3_NUM_TIMERS; n++) {
+
+ timer = &sc->sc_timers[n];
+
+ /* First try and get the register addresses, if this fails we assume
+ * there are no more timers.
+ */
+ rid = n;
+ timer->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (timer->mem_res == NULL)
+ break;
+
+ /* Next try and get the interrupt resource, this is not fatal */
+ rid = n;
+ timer->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ // I decided to delete support for OMAP4 timers from the original code - aleek
+ rev = omap_gptimer_readl(timer, OMAP_GPT_TIDR);
+ switch (rev) {
+ case 0x00000013: /* OMAP3 without 1ms generation */
+ case 0x00000015:
+ case 0x00000021: /* OMAP3 with 1ms generation */
+ timer->profile = OMAP_GPTIMER_PROFILE_OMAP3;
+ break;
+ break;
+ default:
+ timer->profile = OMAP_GPTIMER_PROFILE_UNKNOWN;
+ break;
+ }
+
+ if (timer->profile == OMAP_GPTIMER_PROFILE_UNKNOWN) {
+ device_printf(dev, "Error: failed to determine the type of "
+ "GPTIMER%d, ignoring timer (rev: 0x%08x)\n", (n + 1),
+ rev);
+ break;
+ }
+ /* 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.
+ */
+ timer->source = (GPTIMER1_CLK + n);
+
+ /* Finally mark the timer as available */
+ timer->flags = OMAP_GPTIMER_AVAILABLE_FLAG;
+ }
+
+ /* Store the number of timers installed */
+ sc->sc_num_timers = n;
+
+ /* Store the timer structure globally - this driver should never be unloaded */
+ g_omap_gptimer_sc = sc;
+
+ /* setup GPTIMER10 for system ticks, and GPTIMER11 for general purpose counter */
+
+ oldirqstate = disable_interrupts(I32_bit);
+
+ /* Number of microseconds between interrupts */
+ tick = 1000000 / hz;
+
+ /* Next setup one of the timers to be the system tick timer */
+ if (omap3_gptimer_activate(TICKTIMER_GPTIMER, OMAP_GPTIMER_PERIODIC_FLAG,
+ tick, NULL, NULL)) {
+ panic("Error: failed to activate system tick timer\n");
+ }
+
+ /* Setup an interrupt filter for the timer */
+ if (omap_gptimer_set_intr_filter(TICKTIMER_GPTIMER, omap3_timer_tick_intr))
+ panic("Error: failed to start system tick timer\n");
+
+ /* Lastly start the tick timer */
+ if (omap_gptimer_start(TICKTIMER_GPTIMER))
+ panic("Error: failed to start system tick timer\n");
+
+ omap3_gptimer_get_freq(TICKTIMER_GPTIMER, &timer_freq);
+ printf("tick: timer_freq = %u\n", timer_freq);
+
+
+
+ /* Setup another timer to be the timecounter */
+ if (omap3_gptimer_activate(TIMECOUNT_GPTIMER, OMAP_GPTIMER_PERIODIC_FLAG, 0,
+ NULL, NULL)) {
+ printf("Error: failed to activate system tick timer\n");
+ } else if (omap_gptimer_start(TIMECOUNT_GPTIMER)) {
+ printf("Error: failed to start system tick timer\n");
+ }
+
+ /* Save the system clock speed */
+ omap3_gptimer_get_freq(TIMECOUNT_GPTIMER, &timer_freq);
+ g_omap3_timecounter.tc_frequency = timer_freq;
+
+ /* Setup the time counter */
+ tc_init(&g_omap3_timecounter);
+
+ /* Calibrate the delay loop */
+ omap3_calibrate_delay_loop(&g_omap3_timecounter);
+
+ /* Restore interrupt state */
+ restore_interrupts(oldirqstate);
+
+}
+
+static device_method_t g_omap_gptimer_methods[] = {
+ DEVMETHOD(device_probe, omap_gptimer_probe),
+ DEVMETHOD(device_attach, omap_gptimer_attach),
+ {0, 0},
+};
+
+static driver_t g_omap_gptimer_driver = {
+ "omap_gptimer",
+ g_omap_gptimer_methods,
+ sizeof(struct omap_gptimer_softc),
+};
+static devclass_t g_omap_gptimer_devclass;
+
+DRIVER_MODULE(omap_gptimer, omap, g_omap_gptimer_driver, g_omap_gptimer_devclass, 0, 0);
+MODULE_DEPEND(omap_gptimer, ti_prcm, 1, 1, 1);
+
+}
Modified: soc2012/aleek/beaglexm-armv6/sys/arm/ti/ti_prcm.c
==============================================================================
--- soc2012/aleek/beaglexm-armv6/sys/arm/ti/ti_prcm.c Tue Jun 12 11:08:51 2012 (r237542)
+++ soc2012/aleek/beaglexm-armv6/sys/arm/ti/ti_prcm.c Tue Jun 12 12:24:55 2012 (r237543)
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2010
- * Ben Gray <ben.r.gray at gmail.com>.
+ * Ben Gray <ben.r.gray at gmail.c m>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
More information about the svn-soc-all
mailing list