Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 12 Jun 2012 12:24:55 +0000
From:      aleek@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r237543 - in soc2012/aleek/beaglexm-armv6/sys/arm/ti: . am37x
Message-ID:  <20120612122455.D16731065673@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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@gmail.com>.
+ *	Ben Gray <ben.r.gray@gmail.c m>.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120612122455.D16731065673>