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>