Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/omap New driver for OMAP-style dual-mode timers...



details:   https://anonhg.NetBSD.org/src/rev/ef6951d95040
branches:  trunk
changeset: 783218:ef6951d95040
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Tue Dec 11 19:01:18 2012 +0000

description:
New driver for OMAP-style dual-mode timers, for both v1 and v2 cores.

diffstat:

 sys/arch/arm/omap/omap_dmtimer.c    |  407 ++++++++++++++++++++++++++++++++++++
 sys/arch/arm/omap/omap_dmtimerreg.h |  167 ++++++++++++++
 sys/arch/arm/omap/omap_dmtimervar.h |   62 +++++
 3 files changed, 636 insertions(+), 0 deletions(-)

diffs (truncated from 648 to 300 lines):

diff -r 2daee50257d3 -r ef6951d95040 sys/arch/arm/omap/omap_dmtimer.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/arch/arm/omap/omap_dmtimer.c  Tue Dec 11 19:01:18 2012 +0000
@@ -0,0 +1,407 @@
+/*     $NetBSD: omap_dmtimer.c,v 1.1 2012/12/11 19:01:18 riastradh Exp $       */
+
+/*
+ * TI OMAP Dual-mode timers
+ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: omap_dmtimer.c,v 1.1 2012/12/11 19:01:18 riastradh Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/atomic.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/timetc.h>
+
+#include <machine/intr.h>
+
+#include <arm/omap/omap2_prcm.h>
+#include <arm/omap/omap_dmtimerreg.h>
+#include <arm/omap/omap_dmtimervar.h>
+
+typedef uint8_t dmt_reg_t;
+typedef uint16_t dmt_timer_reg_t;
+
+static unsigned int
+               dmt_tc_get_timecount(struct timecounter *);
+static int     dmt_hardintr(void *);
+static int     dmt_statintr(void *);
+static void    dmt_start_periodic_intr(struct omap_dmtimer_softc *, int,
+                   unsigned int, int (*)(void *));
+static void    dmt_set_periodic_intr_frequency(struct omap_dmtimer_softc *,
+                   unsigned int);
+static void    dmt_start_timecounter(struct omap_dmtimer_softc *);
+static unsigned int
+               dmt_get_timecount(struct omap_dmtimer_softc *);
+static void    dmt_start(struct omap_dmtimer_softc *, unsigned int);
+static void    dmt_reset(struct omap_dmtimer_softc *);
+static void    dmt_enable(struct omap_dmtimer_softc *);
+static void    dmt_intr_enable(struct omap_dmtimer_softc *, uint32_t);
+static void    dmt_intr_ack(struct omap_dmtimer_softc *, uint32_t);
+static uint32_t        dmt_timer_read_4(struct omap_dmtimer_softc *, dmt_timer_reg_t);
+static void    dmt_timer_write_4(struct omap_dmtimer_softc *, dmt_timer_reg_t,
+                   uint32_t);
+static void    dmt_timer_write_post_wait(struct omap_dmtimer_softc *,
+                   dmt_timer_reg_t);
+static uint32_t        dmt_read_4(struct omap_dmtimer_softc *, dmt_reg_t);
+static void    dmt_write_4(struct omap_dmtimer_softc *, dmt_reg_t, uint32_t);
+
+static struct omap_dmtimer_softc *hardclock_sc;
+static struct omap_dmtimer_softc *statclock_sc;
+static struct timecounter dmt_timecounter;
+
+void
+omap_dmtimer_attach_timecounter(struct omap_dmtimer_softc *sc)
+{
+
+       if (dmt_timecounter.tc_priv != NULL)
+               panic("omap dmtimer timecounter already initialized");
+
+       dmt_timecounter.tc_priv = sc;
+}
+
+static struct timecounter dmt_timecounter = {
+       .tc_get_timecount       = dmt_tc_get_timecount,
+       .tc_counter_mask        = 0xffffffff, /* XXXMAGIC Make sense?  */
+       .tc_frequency           = OMAP_SYSTEM_CLOCK_FREQ, /* XXXPOWER */
+       .tc_name                = "dmtimer", /* XXX Which one?  */
+       .tc_quality             = 100, /* XXXMAGIC?  */
+       .tc_priv                = NULL,
+};
+
+static unsigned int
+dmt_tc_get_timecount(struct timecounter *tc)
+{
+       struct omap_dmtimer_softc *sc = tc->tc_priv;
+
+       if (sc == NULL)
+               panic("uninitialized omap dmtimer timecounter");
+
+       return dmt_get_timecount(sc);
+}
+
+void
+omap_dmtimer_attach_hardclock(struct omap_dmtimer_softc *sc)
+{
+
+       if (hardclock_sc != NULL)
+               panic("%s: replacing hardclock %s", device_xname(sc->sc_dev),
+                   device_xname(hardclock_sc->sc_dev));
+       hardclock_sc = sc;
+}
+
+void
+omap_dmtimer_attach_statclock(struct omap_dmtimer_softc *sc)
+{
+
+       KASSERT(stathz != 0);
+       if (statclock_sc != NULL)
+               panic("%s: replacing statclock %s", device_xname(sc->sc_dev),
+                   device_xname(statclock_sc->sc_dev));
+       statclock_sc = sc;
+}
+
+void
+cpu_initclocks(void)
+{
+       struct omap_dmtimer_softc *timecounter_sc = dmt_timecounter.tc_priv;
+
+       if (hardclock_sc == NULL)
+               panic("omap dmtimer hardclock not initialized");
+       dmt_enable(hardclock_sc);
+       dmt_start_periodic_intr(hardclock_sc, IPL_CLOCK, hz, &dmt_hardintr);
+
+       if (timecounter_sc == NULL)
+               panic("omap dmtimer timecounter not initialized");
+       dmt_enable(statclock_sc);
+       dmt_start_periodic_intr(statclock_sc, IPL_HIGH, stathz, &dmt_statintr);
+
+       if (statclock_sc == NULL)
+               panic("omap dmtimer statclock not initialized");
+       dmt_enable(timecounter_sc);
+       dmt_start_timecounter(timecounter_sc);
+       tc_init(&dmt_timecounter);
+}
+
+void
+setstatclockrate(int rate)
+{
+       struct omap_dmtimer_softc *sc = statclock_sc;
+
+       if (rate < 0)
+               panic("I can't run the statistics clock backward!");
+
+       if (sc == NULL)
+               panic("There is no statclock timer!\n");
+
+       dmt_set_periodic_intr_frequency(sc, rate);
+}
+
+static int
+dmt_hardintr(void *frame)
+{
+       struct omap_dmtimer_softc *sc = hardclock_sc;
+
+       KASSERT(sc != NULL);
+       dmt_intr_ack(sc, OMAP_DMTIMER_INTR_ALL);
+       hardclock(frame);
+
+       return 1;
+}
+
+static int
+dmt_statintr(void *frame)
+{
+       struct omap_dmtimer_softc *sc = statclock_sc;
+
+       KASSERT(sc != NULL);
+       dmt_intr_ack(sc, OMAP_DMTIMER_INTR_ALL);
+       statclock(frame);
+
+       return 1;
+}
+
+static void
+dmt_start_periodic_intr(struct omap_dmtimer_softc *sc, int ipl,
+    unsigned int frequency, int (*func)(void *))
+{
+
+       dmt_reset(sc);
+       dmt_start(sc, frequency);
+       /*
+        * Null argument means func gets the interrupt frame.  For
+        * whatever reason it's not an option to pass an argument (such
+        * as sc) and the interrupt frame both, which is why we have
+        * the global hardclock_sc and statclock_sc.
+        */
+       intr_establish(sc->sc_intr, ipl, IST_LEVEL, func, 0);
+       dmt_intr_enable(sc, OMAP_DMTIMER_INTR_OVERFLOW);
+}
+
+static void
+dmt_set_periodic_intr_frequency(struct omap_dmtimer_softc *sc,
+    unsigned int frequency)
+{
+
+       dmt_reset(sc);
+       dmt_start(sc, frequency);
+       dmt_intr_enable(sc, OMAP_DMTIMER_INTR_OVERFLOW);
+}
+
+static void
+dmt_start_timecounter(struct omap_dmtimer_softc *sc)
+{
+
+       /*
+        * XXXPOWER On reset, the timer uses the system clock.  For
+        * low-power operation, we can configure timers to use less
+        * frequent clocks, but there's currently no abstraction for
+        * doing this.
+        */
+       dmt_reset(sc);
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_LOAD, 0);
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_COUNTER, 0);
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_CTRL,
+           (OMAP_DMTIMER_TIMER_CTRL_START |
+               OMAP_DMTIMER_TIMER_CTRL_AUTORELOAD));
+}
+
+static unsigned int
+dmt_get_timecount(struct omap_dmtimer_softc *sc)
+{
+
+       return dmt_timer_read_4(sc, OMAP_DMTIMER_TIMER_COUNTER);
+}
+
+static void
+dmt_start(struct omap_dmtimer_softc *sc, unsigned int frequency)
+{
+       uint32_t value;
+
+       /*
+        * XXXPOWER Should do something clever with prescaling and
+        * clock selection to save power.
+        */
+
+       /*
+        * XXX The dmtimer doesn't even necessarily run at the system
+        * clock frequency by default.  On the AM335x, the system clock
+        * frequency is 24 MHz, but dmtimer0 runs at 32 kHz.
+        */
+       value = (0xffffffff - ((OMAP_SYSTEM_CLOCK_FREQ / frequency) - 1));
+
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_LOAD, value);
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_COUNTER, value);
+
+       dmt_timer_write_4(sc, OMAP_DMTIMER_TIMER_CTRL,
+           (OMAP_DMTIMER_TIMER_CTRL_START |
+               OMAP_DMTIMER_TIMER_CTRL_AUTORELOAD));
+}
+
+static void
+dmt_reset(struct omap_dmtimer_softc *sc)
+{
+       uint32_t reset_mask;
+       unsigned int tries = 1000; /* XXXMAGIC */
+
+       if (sc->sc_version == 1)
+               reset_mask = OMAP_DMTIMER_V1_OCP_CFG_SOFTRESET_MASK;
+       else
+               reset_mask = OMAP_DMTIMER_V2_OCP_CFG_SOFTRESET_MASK;
+
+       dmt_write_4(sc, OMAP_DMTIMER_OCP_CFG, reset_mask);
+       while (dmt_read_4(sc, OMAP_DMTIMER_OCP_CFG) & reset_mask) {
+               if (--tries == 0)
+                       panic("unable to reset dmtimer %p", sc);
+               DELAY(10);      /* XXXMAGIC */
+       }
+
+       /*
+        * Posted mode is enabled on reset on the OMAP35x but disabled
+        * on reset on the AM335x, so handle both cases.
+        *
+        * XXXPOWER Does enabling this reduce power consumption?
+        */
+       sc->sc_posted =



Home | Main Index | Thread Index | Old Index