summaryrefslogtreecommitdiffstats
path: root/sys/arm/econa/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/econa/timer.c')
-rw-r--r--sys/arm/econa/timer.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/sys/arm/econa/timer.c b/sys/arm/econa/timer.c
new file mode 100644
index 0000000..e7cca79
--- /dev/null
+++ b/sys/arm/econa/timer.c
@@ -0,0 +1,382 @@
+/*-
+ * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>.
+ * 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 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 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include "econa_reg.h"
+#include "econa_var.h"
+
+#define INITIAL_TIMECOUNTER (0xffffffff)
+
+static int timers_initialized = 0;
+
+#define HZ 100
+
+extern unsigned int CPU_clock;
+extern unsigned int AHB_clock;
+extern unsigned int APB_clock;
+
+static unsigned long timer_counter = 0;
+
+struct ec_timer_softc {
+ struct resource * timer_res[3];
+ bus_space_tag_t timer_bst;
+ bus_space_handle_t timer_bsh;
+ struct mtx timer_mtx;
+};
+
+static struct resource_spec ec_timer_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static unsigned ec_timer_get_timecount(struct timecounter *);
+
+static struct timecounter ec_timecounter = {
+ .tc_get_timecount = ec_timer_get_timecount,
+ .tc_name = "CPU Timer",
+ /* This is assigned on the fly in the init sequence */
+ .tc_frequency = 0,
+ .tc_counter_mask = ~0u,
+ .tc_quality = 1000,
+};
+
+static struct ec_timer_softc *timer_softc = NULL;
+
+static inline
+void write_4(unsigned int val, unsigned int addr)
+{
+ bus_space_write_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, addr, val);
+
+}
+
+static inline
+unsigned int read_4(unsigned int addr)
+{
+
+ return bus_space_read_4(timer_softc->timer_bst,
+ timer_softc->timer_bsh, addr);
+}
+
+#define uSECS_PER_TICK (1000000 / APB_clock)
+#define TICKS2USECS(x) ((x) * uSECS_PER_TICK)
+
+static unsigned
+read_timer_counter_noint(void)
+{
+
+ arm_mask_irq(0);
+ unsigned int v = read_4(TIMER_TM1_COUNTER_REG);
+ arm_unmask_irq(0);
+ return v;
+}
+
+void
+DELAY(int usec)
+{
+ uint32_t val, val_temp;
+ int nticks;
+
+ if (!timers_initialized) {
+ for (; usec > 0; usec--)
+ for (val = 100; val > 0; val--)
+ ;
+ return;
+ }
+
+ val = read_timer_counter_noint();
+ nticks = (((APB_clock / 1000) * usec) / 1000) + 100;
+
+ while (nticks > 0) {
+ val_temp = read_timer_counter_noint();
+ if (val > val_temp)
+ nticks -= (val - val_temp);
+ else
+ nticks -= (val + (timer_counter - val_temp));
+
+ val = val_temp;
+ }
+
+}
+
+/*
+ * Setup timer
+ */
+static inline void
+setup_timer(unsigned int counter_value)
+{
+ unsigned int control_value;
+ unsigned int mask_value;
+
+ control_value = read_4(TIMER_TM_CR_REG);
+
+ mask_value = read_4(TIMER_TM_INTR_MASK_REG);
+ write_4(counter_value, TIMER_TM1_COUNTER_REG);
+ write_4(counter_value, TIMER_TM1_LOAD_REG);
+ write_4(0, TIMER_TM1_MATCH1_REG);
+ write_4(0,TIMER_TM1_MATCH2_REG);
+
+ control_value &= ~(TIMER1_CLOCK_SOURCE);
+ control_value |= TIMER1_UP_DOWN_COUNT;
+
+ write_4(0, TIMER_TM2_COUNTER_REG);
+ write_4(0, TIMER_TM2_LOAD_REG);
+ write_4(~0u, TIMER_TM2_MATCH1_REG);
+ write_4(~0u,TIMER_TM2_MATCH2_REG);
+
+ control_value &= ~(TIMER2_CLOCK_SOURCE);
+ control_value &= ~(TIMER2_UP_DOWN_COUNT);
+
+ mask_value &= ~(63);
+
+ write_4(control_value, TIMER_TM_CR_REG);
+ write_4(mask_value, TIMER_TM_INTR_MASK_REG);
+}
+
+/*
+ * Enable timer
+ */
+static inline void
+timer_enable(void)
+{
+ unsigned int control_value;
+
+ control_value = read_4(TIMER_TM_CR_REG);
+
+ control_value |= TIMER1_OVERFLOW_ENABLE;
+ control_value |= TIMER1_ENABLE;
+ control_value |= TIMER2_OVERFLOW_ENABLE;
+ control_value |= TIMER2_ENABLE;
+
+ write_4(control_value, TIMER_TM_CR_REG);
+}
+
+static inline unsigned int
+read_second_timer_counter(void)
+{
+
+ return read_4(TIMER_TM2_COUNTER_REG);
+}
+
+/*
+ * Get timer interrupt status
+ */
+static inline unsigned int
+read_timer_interrupt_status(void)
+{
+
+ return read_4(TIMER_TM_INTR_STATUS_REG);
+}
+
+/*
+ * Clear timer interrupt status
+ */
+static inline void
+clear_timer_interrupt_status(unsigned int irq)
+{
+ unsigned int interrupt_status;
+
+ interrupt_status = read_4(TIMER_TM_INTR_STATUS_REG);
+ if (irq == 0) {
+ if (interrupt_status & (TIMER1_MATCH1_INTR))
+ interrupt_status &= ~(TIMER1_MATCH1_INTR);
+ if (interrupt_status & (TIMER1_MATCH2_INTR))
+ interrupt_status &= ~(TIMER1_MATCH2_INTR);
+ if (interrupt_status & (TIMER1_OVERFLOW_INTR))
+ interrupt_status &= ~(TIMER1_OVERFLOW_INTR);
+ }
+ if (irq == 1) {
+ if (interrupt_status & (TIMER2_MATCH1_INTR))
+ interrupt_status &= ~(TIMER2_MATCH1_INTR);
+ if (interrupt_status & (TIMER2_MATCH2_INTR))
+ interrupt_status &= ~(TIMER2_MATCH2_INTR);
+ if (interrupt_status & (TIMER2_OVERFLOW_INTR))
+ interrupt_status &= ~(TIMER2_OVERFLOW_INTR);
+ }
+
+ write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG);
+}
+
+static unsigned
+ec_timer_get_timecount(struct timecounter *a)
+{
+ unsigned int ticks1;
+ arm_mask_irq(1);
+ ticks1 = read_second_timer_counter();
+ arm_unmask_irq(1);
+ return ticks1;
+}
+
+/*
+ * Setup timer
+ */
+static inline void
+do_setup_timer(void)
+{
+
+ timer_counter = APB_clock/HZ;
+ /*
+ * setup timer-related values
+ */
+ setup_timer(timer_counter);
+}
+
+void
+cpu_initclocks(void)
+{
+
+ ec_timecounter.tc_frequency = APB_clock;
+ tc_init(&ec_timecounter);
+ timer_enable();
+ timers_initialized = 1;
+}
+
+void
+cpu_startprofclock(void)
+{
+
+}
+
+void
+cpu_stopprofclock(void)
+{
+
+}
+
+static int
+ec_timer_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Econa CPU Timer");
+ return (0);
+}
+
+static int
+ec_reset(void *arg)
+{
+
+ arm_mask_irq(1);
+ clear_timer_interrupt_status(1);
+ arm_unmask_irq(1);
+ return (FILTER_HANDLED);
+}
+
+static int
+ec_hardclock(void *arg)
+{
+ struct trapframe *frame;
+ unsigned int val;
+ /*clear timer interrupt status*/
+
+ arm_mask_irq(0);
+
+ val = read_4(TIMER_INTERRUPT_STATUS_REG);
+ val &= ~(TIMER1_OVERFLOW_INTERRUPT);
+ write_4(val, TIMER_INTERRUPT_STATUS_REG);
+
+ frame = (struct trapframe *)arg;
+ hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
+
+ arm_unmask_irq(0);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+ec_timer_attach(device_t dev)
+{
+ struct ec_timer_softc *sc;
+ int error;
+ void *ihl;
+
+
+ if (timer_softc != NULL)
+ return (ENXIO);
+
+ sc = (struct ec_timer_softc *)device_get_softc(dev);
+
+ timer_softc = sc;
+
+ error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res);
+ if (error) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
+ sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
+
+ do_setup_timer();
+
+ if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
+ ec_hardclock, NULL, NULL, &ihl) != 0) {
+ bus_release_resources(dev, ec_timer_spec, sc->timer_res);
+ device_printf(dev, "could not setup hardclock interrupt\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK,
+ ec_reset, NULL, NULL, &ihl) != 0) {
+ bus_release_resources(dev, ec_timer_spec, sc->timer_res);
+ device_printf(dev, "could not setup timer interrupt\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static device_method_t ec_timer_methods[] = {
+ DEVMETHOD(device_probe, ec_timer_probe),
+ DEVMETHOD(device_attach, ec_timer_attach),
+ { 0, 0 }
+};
+
+static driver_t ec_timer_driver = {
+ "timer",
+ ec_timer_methods,
+ sizeof(struct ec_timer_softc),
+};
+
+static devclass_t ec_timer_devclass;
+
+DRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0);
OpenPOWER on IntegriCloud