summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2011-11-05 11:48:27 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2011-11-05 11:48:27 +0000
commit763bcc3c07d7dcc15704a953a3ac6dd9c6e06ffe (patch)
tree2f1b430bcfc2d3dd7cec4666f2f791e6c5c43782
parent20cde4e5e2b5314310b4a9be3dbca42719665373 (diff)
Initial drivers for Cortex A9 interrupt controller and global timer as clock.
-rw-r--r--sys/arch/beagle/dev/ampintc.c581
-rw-r--r--sys/arch/beagle/dev/amptimer.c283
2 files changed, 864 insertions, 0 deletions
diff --git a/sys/arch/beagle/dev/ampintc.c b/sys/arch/beagle/dev/ampintc.c
new file mode 100644
index 00000000000..b0b14fb7f7f
--- /dev/null
+++ b/sys/arch/beagle/dev/ampintc.c
@@ -0,0 +1,581 @@
+/* $OpenBSD: ampintc.c,v 1.1 2011/11/05 11:48:26 drahn Exp $ */
+/*
+ * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This driver implements the interrupt controller as specified in
+ * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
+ * IHI0048A_gic_architecture_spec_v1_0 underlying specification
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+#include <arm/cpufunc.h>
+#include <machine/bus.h>
+#include <arch/beagle/beagle/ahb.h>
+
+/* registers */
+#define ICD_DCR 0x000
+#define ICD_DCR_ES 0x00000001
+#define ICD_DCR_ENS 0x00000002
+
+#define ICD_ICTR 0x004
+#define ICD_ICTR_LSPI_SH 11
+#define ICD_ICTR_LSPI_M 0x1f
+#define ICD_ICTR_CPU_SH 5
+#define ICD_ICTR_CPU_M 0x07
+#define ICD_ICTR_ITL_SH 0
+#define ICD_ICTR_ITL_M 0x1f
+#define ICD_IDIR 0x008
+#define ICD_DIR_PROD_SH 24
+#define ICD_DIR_PROD_M 0xff
+#define ICD_DIR_REV_SH 12
+#define ICD_DIR_REV_M 0xfff
+#define ICD_DIR_IMP_SH 0
+#define ICD_DIR_IMP_M 0xfff
+
+#define IRQ_TO_REG32(i) (((i) >> 5) & 0x7)
+#define IRQ_TO_REG32BIT(i) ((i) & 0x1f)
+#define IRQ_TO_REG4(i) (((i) >> 2) & 0x3f)
+#define IRQ_TO_REG4BIT(i) ((i) & 0x3)
+#define IRQ_TO_REG16(i) (((i) >> 4) & 0xf)
+#define IRQ_TO_REGBIT_S(i) 8
+#define IRQ_TO_REG4BIT_M(i) 8
+
+#define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4))
+#define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4))
+#define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4))
+#define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4))
+#define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4))
+#define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4))
+#define ICD_IPRn(i) (0x400 + (i))
+#define ICD_IPTRn(i) (0x800 + (IRQ_TO_REG4(i) * 4))
+#define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) / 4))
+/*
+ * what about (ppi|spi)_status
+ */
+#define ICD_PPI 0xD00
+#define ICD_PPI_GTIMER (1 << 11)
+#define ICD_PPI_FIQ (1 << 12)
+#define ICD_PPI_PTIMER (1 << 13)
+#define ICD_PPI_PWDOG (1 << 14)
+#define ICD_PPI_IRQ (1 << 15)
+#define ICD_SPI_BASE 0xD04
+#define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4))
+
+
+#define ICD_SGIR 0xF00
+
+#define ICD_PERIPH_ID_0 0xFD0
+#define ICD_PERIPH_ID_1 0xFD4
+#define ICD_PERIPH_ID_2 0xFD8
+#define ICD_PERIPH_ID_3 0xFDC
+#define ICD_PERIPH_ID_4 0xFE0
+#define ICD_PERIPH_ID_5 0xFE4
+#define ICD_PERIPH_ID_6 0xFE8
+#define ICD_PERIPH_ID_7 0xFEC
+
+#define ICD_COMP_ID_0 0xFEC
+#define ICD_COMP_ID_1 0xFEC
+#define ICD_COMP_ID_2 0xFEC
+#define ICD_COMP_ID_3 0xFEC
+
+#define ICD_SIZE 0x1000
+
+
+#define ICPICR 0x00
+#define ICPIPMR 0x04
+/* XXX - must left justify bits to 0 - 7 */
+#define ICMIPMR_SH 4
+#define ICPBPR 0x08
+#define ICPIAR 0x0C
+#define ICPIAR_IRQ_SH 0
+#define ICPIAR_IRQ_M 0x3ff
+#define ICPIAR_CPUID_SH 10
+#define ICPIAR_CPUID_M 0x7
+#define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M
+#define ICPEOIR 0x10
+#define ICPPRP 0x14
+#define ICPHPIR 0x18
+#define ICPIIR 0xFC
+#define ICP_SIZE 0x100
+/*
+ * what about periph_id and component_id
+ */
+
+#define AMPAMPINTC_SIZE 0x1000
+
+#define NIRQ 256
+
+struct ampintc_softc {
+ struct device sc_dev;
+ struct intrq *sc_ampintc_handler;
+ int sc_nintr;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_d_ioh, sc_p_ioh;
+};
+struct ampintc_softc *ampintc;
+
+
+struct intrhand {
+ TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
+ int (*ih_func)(void *); /* handler */
+ void *ih_arg; /* arg for handler */
+ int ih_ipl; /* IPL_* */
+ int ih_irq; /* IRQ number */
+ struct evcount ih_count;
+ char *ih_name;
+};
+
+struct intrq {
+ TAILQ_HEAD(, intrhand) iq_list; /* handler list */
+ int iq_irq; /* IRQ to mask while handling */
+ int iq_levels; /* IPL_*'s this IRQ has */
+ int iq_ist; /* share type */
+};
+
+
+int ampintc_match(struct device *, void *, void *);
+void ampintc_attach(struct device *, struct device *, void *);
+int ampintc_spllower(int);
+void ampintc_splx(int);
+int ampintc_splraise(int);
+void ampintc_setipl(int);
+void ampintc_calc_mask(void);
+void *ampintc_intr_establish(int, int, int (*)(void *), void *,
+ char *);
+void ampintc_intr_disestablish(void *);
+void ampintc_irq_handler(void *);
+const char *ampintc_intr_string(void *);
+uint32_t ampintc_iack(void);
+void ampintc_eoi(uint32_t);
+void ampintc_set_priority(int, int);
+void ampintc_intr_enable(int);
+void ampintc_intr_disable(int);
+
+struct cfattach ampintc_ca = {
+ sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
+};
+
+struct cfdriver ampintc_cd = {
+ NULL, "ampintc", DV_DULL
+};
+
+int ampintc_attached = 0;
+
+int
+ampintc_match(struct device *parent, void *v, void *aux)
+{
+ printf("ampintc_match board %d\n", board_id);
+ switch (board_id) {
+ case BOARD_ID_OMAP3_BEAGLE:
+ return 0; /* not ported yet ??? - different */
+ case BOARD_ID_OMAP4_PANDA:
+ printf("Yea?\n");
+ break; /* continue trying */
+ default:
+ return 0; /* unknown */
+ }
+ printf("Yea1\n");
+ if (ampintc_attached != 0)
+ return 0;
+ printf("Yea2\n");
+
+ /* XXX */
+ return (1);
+}
+
+
+void
+ampintc_attach(struct device *parent, struct device *self, void *args)
+{
+ struct ahb_attach_args *aa = args;
+ struct ampintc_softc *sc = (struct ampintc_softc *)self;
+ int i, nintr;
+ bus_space_tag_t iot;
+ bus_space_handle_t d_ioh, p_ioh;
+
+ ampintc = sc;
+
+ arm_init_smask();
+
+#define ICD_ADDR 0x48241000
+#define ICP_ADDR 0x48240100
+ iot = aa->aa_iot;
+ if (bus_space_map(iot, ICD_ADDR, ICD_SIZE, 0, &d_ioh))
+ panic("ampintc_attach: bus_space_map failed!");
+
+ if (bus_space_map(iot, ICP_ADDR, ICP_SIZE, 0, &p_ioh))
+ panic("ampintc_attach: bus_space_map failed!");
+
+ sc->sc_d_ioh = d_ioh;
+ sc->sc_p_ioh = p_ioh;
+
+ nintr = 32 * (bus_space_read_4(iot, d_ioh, ICD_ICTR) & ICD_ICTR_ITL_M);
+ nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
+ sc->sc_nintr = nintr;
+ printf(" nirq %d\n", nintr);
+ if (nintr > NIRQ) {
+ panic("ampintc needs NIRQ bumped to at least %d", nintr);
+ /*
+ * or should this code just dynamically allocate
+ * ampintc_handler[]
+ */
+ }
+ printf("periph_id 0 %x\n",
+ bus_space_read_1(iot, d_ioh, ICD_PERIPH_ID_0));
+ printf("periph_id 1 %x\n",
+ bus_space_read_1(iot, d_ioh, ICD_PERIPH_ID_1));
+ printf("periph_id 2 %x\n",
+ bus_space_read_1(iot, d_ioh, ICD_PERIPH_ID_2));
+ printf("periph_id 3 %x\n",
+ bus_space_read_1(iot, d_ioh, ICD_PERIPH_ID_3));
+ printf("periph_id 4 %x\n",
+ bus_space_read_1(iot, d_ioh, ICD_PERIPH_ID_4));
+
+ /* Disable all interrupts, clear all pending */
+ for (i = 0; i < nintr/32; i++) {
+ bus_space_write_4(iot, d_ioh, ICD_ICERn(i*32), ~0);
+ bus_space_write_4(iot, d_ioh, ICD_ICPRn(i*32), ~0);
+ }
+ for (i = 0; i < nintr/4; i++) {
+
+ /* lowest priority ?? */
+ bus_space_write_4(iot, d_ioh, ICD_IPRn(i*32), 0xffffffff);
+ /* target no cpus */
+ bus_space_write_4(iot, d_ioh, ICD_IPTRn(i*32), 0);
+ }
+ for (i = 2; i < nintr/15; i++) {
+ /* irq 32 - N */
+ bus_space_write_4(iot, d_ioh, ICD_ICRn(i*32), 0);
+ }
+
+ /* software reset of the part? */
+ /* set protection bit (kernel only)? */
+
+ /* XXX - check power saving bit */
+
+
+ sc->sc_ampintc_handler = malloc(
+ (sizeof (*sc->sc_ampintc_handler) * nintr),
+ M_DEVBUF, M_ZERO);
+ for (i = 0; i < nintr; i++) {
+
+ TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list);
+ }
+
+ ampintc_calc_mask();
+
+ ampintc_attached = 1;
+
+ /* insert self as interrupt handler */
+ arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
+ ampintc_setipl, ampintc_intr_establish, ampintc_intr_disestablish,
+ ampintc_intr_string, ampintc_irq_handler);
+
+ ampintc_setipl(IPL_HIGH); /* XXX ??? */
+ enable_interrupts(I32_bit);
+}
+
+void
+ampintc_set_priority(int irq, int pri)
+{
+ struct ampintc_softc *sc = ampintc;
+ uint32_t prival;
+
+ /*
+ * We only use 16 (13 really) interrupt priorities,
+ * and a CPU is only required to implement bit 4-7 of each field
+ * so shift into the top bits.
+ * also low values are higher priority thus IPL_HIGH - pri
+ */
+ prival = (IPL_HIGH - pri) << ICMIPMR_SH;
+ bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
+}
+
+void
+ampintc_setipl(int new)
+{
+ struct cpu_info *ci = curcpu();
+ struct ampintc_softc *sc = ampintc;
+ int psw;
+
+ /* disable here is only to keep hardware in sync with ci->ci_cpl */
+ psw = disable_interrupts(I32_bit);
+ ci->ci_cpl = new;
+ /* low values are higher priority thus IPL_HIGH - pri */
+
+ bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
+ (IPL_HIGH - new) << ICMIPMR_SH);
+ restore_interrupts(psw);
+}
+
+void
+ampintc_intr_enable(int irq)
+{
+ struct ampintc_softc *sc = ampintc;
+
+ bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
+ 1 << IRQ_TO_REG32BIT(irq));
+}
+
+void
+ampintc_intr_disable(int irq)
+{
+ struct ampintc_softc *sc = ampintc;
+
+ bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
+ 1 << IRQ_TO_REG32BIT(irq));
+}
+
+
+void
+ampintc_calc_mask(void)
+{
+ struct cpu_info *ci = curcpu();
+ struct ampintc_softc *sc = ampintc;
+ struct intrhand *ih;
+ int irq;
+
+ for (irq = 0; irq < NIRQ; irq++) {
+ int max = IPL_NONE;
+ int min = IPL_HIGH;
+ TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list,
+ ih_list) {
+ if (ih->ih_ipl > max)
+ max = ih->ih_ipl;
+
+ if (ih->ih_ipl < min)
+ min = ih->ih_ipl;
+ }
+
+ if (sc->sc_ampintc_handler[irq].iq_irq == max) {
+ continue;
+ }
+ sc->sc_ampintc_handler[irq].iq_irq = max;
+
+ if (max == IPL_NONE)
+ min = IPL_NONE;
+
+#ifdef DEBUG_INTC
+ if (min != IPL_NONE) {
+ printf("irq %d to block at %d %d reg %d bit %d\n",
+ irq, max, min, AMPINTC_IRQ_TO_REG(irq),
+ AMPINTC_IRQ_TO_REGi(irq));
+ }
+#endif
+ /* Enable interrupts at lower levels, clear -> enable */
+ /* Set interrupt priority/enable */
+ if (min != IPL_NONE) {
+ ampintc_set_priority(irq, min);
+ ampintc_intr_enable(irq);
+ } else {
+ ampintc_intr_disable(irq);
+ }
+ }
+ ampintc_setipl(ci->ci_cpl);
+}
+
+void
+ampintc_splx(int new)
+{
+ struct cpu_info *ci = curcpu();
+ ampintc_setipl(new);
+
+ if (ci->ci_ipending & arm_smask[ci->ci_cpl])
+ arm_do_pending_intr(ci->ci_cpl);
+}
+
+int
+ampintc_spllower(int new)
+{
+ struct cpu_info *ci = curcpu();
+ int old = ci->ci_cpl;
+ ampintc_splx(new);
+ return (old);
+}
+
+int
+ampintc_splraise(int new)
+{
+ struct cpu_info *ci = curcpu();
+ int old;
+ old = ci->ci_cpl;
+
+ /*
+ * setipl must always be called because there is a race window
+ * where the variable is updated before the mask is set
+ * an interrupt occurs in that window without the mask always
+ * being set, the hardware might not get updated on the next
+ * splraise completely messing up spl protection.
+ */
+ if (old > new)
+ new = old;
+
+ ampintc_setipl(new);
+
+ return (old);
+}
+
+
+uint32_t
+ampintc_iack(void)
+{
+ uint32_t intid;
+ struct ampintc_softc *sc = ampintc;
+ intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
+
+ return (intid);
+}
+
+void
+ampintc_eoi(uint32_t eoi)
+{
+#if 0
+ uint32_t intid;
+ struct ampintc_softc *sc = ampintc;
+ intid = bus_space_write_4(sc->sc_cpu, ICPEOIR, eoi);
+#endif
+}
+
+void
+ampintc_irq_handler(void *frame)
+{
+ struct ampintc_softc *sc = ampintc;
+ struct intrhand *ih;
+ void *arg;
+ uint32_t iack_val;
+ int irq, pri, s;
+
+#ifdef DEBUG_INTC
+ printf("irq %d fired\n", irq);
+#endif
+ iack_val = ampintc_iack();
+ irq = iack_val & ((1 << sc->sc_nintr) - 1);
+
+ pri = sc->sc_ampintc_handler[irq].iq_irq;
+ s = ampintc_splraise(pri);
+ TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) {
+ if (ih->ih_arg != 0)
+ arg = ih->ih_arg;
+ else
+ arg = frame;
+
+ if (ih->ih_func(arg))
+ ih->ih_count.ec_count++;
+
+ }
+ ampintc_eoi(iack_val);
+
+ ampintc_splx(s);
+}
+
+void *
+ampintc_intr_establish(int irqno, int level, int (*func)(void *),
+ void *arg, char *name)
+{
+ struct ampintc_softc *sc = ampintc;
+ struct intrhand *ih;
+ int psw;
+
+ if (irqno < 0 || irqno >= NIRQ)
+ panic("ampintc_intr_establish: bogus irqnumber %d: %s",
+ irqno, name);
+
+ /* no point in sleeping unless someone can free memory. */
+ ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF,
+ cold ? M_NOWAIT : M_WAITOK);
+ if (ih == NULL)
+ panic("intr_establish: can't malloc handler info");
+ ih->ih_func = func;
+ ih->ih_arg = arg;
+ ih->ih_ipl = level;
+ ih->ih_irq = irqno;
+ ih->ih_name = name;
+
+ psw = disable_interrupts(I32_bit);
+
+ TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
+
+ if (name != NULL)
+ evcount_attach(&ih->ih_count, name, &ih->ih_irq);
+
+#ifdef DEBUG_INTC
+ printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
+ name);
+#endif
+ ampintc_calc_mask();
+
+ restore_interrupts(psw);
+ return (ih);
+}
+
+void
+ampintc_intr_disestablish(void *cookie)
+{
+#if 0
+ int psw;
+ struct intrhand *ih = cookie;
+ int irqno = ih->ih_irq;
+ psw = disable_interrupts(I32_bit);
+ TAILQ_REMOVE(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
+ if (ih->ih_name != NULL)
+ evcount_detach(&ih->ih_count);
+ free(ih, M_DEVBUF);
+ restore_interrupts(psw);
+#endif
+}
+
+const char *
+ampintc_intr_string(void *cookie)
+{
+ return "huh?";
+}
+
+
+#if 0
+int ampintc_tst(void *a);
+
+int
+ampintc_tst(void *a)
+{
+ printf("inct_tst called\n");
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, AMPINTC_ISR_CLEAR0, 2);
+ return 1;
+}
+
+void ampintc_test(void);
+void ampintc_test(void)
+{
+ void * ih;
+ printf("about to register handler\n");
+ ih = ampintc_intr_establish(1, IPL_BIO, ampintc_tst, NULL, "intctst");
+
+ printf("about to set bit\n");
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, AMPINTC_ISR_SET0, 2);
+
+ printf("about to clear bit\n");
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, AMPINTC_ISR_CLEAR0, 2);
+
+ printf("about to remove handler\n");
+ ampintc_intr_disestablish(ih);
+
+ printf("done\n");
+}
+#endif
diff --git a/sys/arch/beagle/dev/amptimer.c b/sys/arch/beagle/dev/amptimer.c
new file mode 100644
index 00000000000..ff1b1e7ba9b
--- /dev/null
+++ b/sys/arch/beagle/dev/amptimer.c
@@ -0,0 +1,283 @@
+/* $OpenBSD: amptimer.c,v 1.1 2011/11/05 11:48:26 drahn Exp $ */
+/*
+ * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/timetc.h>
+#include <sys/evcount.h>
+
+#include <arm/cpufunc.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <arch/beagle/beagle/ahb.h>
+
+#define GTIMER_CNT_LOW 0x00
+#define GTIMER_CNT_HIGH 0x04
+#define GTIMER_CTRL 0x08
+#define GTIMER_CTRL_AA (1 << 3)
+#define GTIMER_CTRL_IRQ (1 << 2)
+#define GTIMER_CTRL_COMP (1 << 1)
+#define GTIMER_CTRL_TIMER (1 << 0)
+#define GTIMER_STATUS 0x0c
+#define GTIMER_STATUS_EVENT (1 << 0)
+#define GTIMER_CMP_LOW 0x10
+#define GTIMER_CMP_HIGH 0x14
+#define GTIMER_AUTOINC 0x18
+
+#define GTIMER_SIZE 0x20
+
+/* XXX - PERIPHCLK */
+#define TIMER_FREQUENCY 32768
+
+u_int amptimer_get_timecount(struct timecounter *);
+
+static struct timecounter amptimer_timecounter = {
+ amptimer_get_timecount, NULL, 0x7fffffff, 0, "amptimer", 0, NULL
+};
+
+struct amptimer_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ volatile u_int64_t sc_nexttickevent;
+ volatile u_int64_t sc_nextstatevent;
+ u_int32_t sc_ticks_per_second;
+ u_int32_t sc_ticks_per_intr;
+ u_int32_t sc_ticks_err_cnt;
+ u_int32_t sc_ticks_err_sum;
+ u_int32_t sc_statvar;
+ u_int32_t sc_statmin;
+
+ struct evcount sc_clk_count;
+ struct evcount sc_stat_count;
+
+};
+
+int amptimer_match(struct device *, void *, void *);
+void amptimer_attach(struct device *, struct device *, void *);
+uint64_t amptimer_readcnt64(struct amptimer_softc *sc);
+int amptimer_intr(void *);
+void amptimer_cpu_initclocks(void);
+
+struct cfattach amptimer_ca = {
+ sizeof (struct amptimer_softc), amptimer_match, amptimer_attach
+};
+
+struct cfdriver amptimer_cd = {
+ NULL, "amptimer", DV_DULL
+};
+
+int
+amptimer_match(struct device *parent, void *v, void *aux)
+{
+ switch (board_id) {
+ case BOARD_ID_OMAP3_BEAGLE:
+ break; /* continue trying */
+ case BOARD_ID_OMAP4_PANDA:
+ return 0; /* not ported yet ??? - different */
+ default:
+ return 0; /* unknown */
+ }
+ return (1);
+}
+
+uint64_t
+amptimer_readcnt64(struct amptimer_softc *sc)
+{
+ uint32_t high0, high1, low;
+ bus_space_tag_t iot = sc->sc_iot;
+ bus_space_handle_t ioh = sc->sc_ioh;
+
+ do {
+ high0 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
+ low = bus_space_read_4(iot, ioh, GTIMER_CNT_LOW);
+ high1 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
+ } while (high0 != high1);
+
+ return ((((uint64_t)high1) << 32) | low);
+}
+
+
+void
+amptimer_attach(struct device *parent, struct device *self, void *args)
+{
+ struct amptimer_softc *sc = (struct amptimer_softc *)self;
+ struct ahb_attach_args *aa = args;
+ bus_space_handle_t ioh;
+
+ sc->sc_iot = aa->aa_iot;
+
+ if (bus_space_map(sc->sc_iot, aa->aa_addr, GTIMER_SIZE, 0, &ioh))
+ panic("amptimer_attach: bus_space_map failed!");
+
+ sc->sc_ioh = ioh;
+
+ /* disable global timer */
+ bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, 0);
+
+ /* XXX ??? reset counters to 0 - gives us uptime in the counter */
+ bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_LOW, 0);
+ bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_HIGH, 0);
+
+ /* enable global timer */
+ bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, GTIMER_CTRL_TIMER);
+
+ /* clear event */
+ bus_space_write_4(sc->sc_iot, ioh, GTIMER_STATUS, 1);
+
+ /*
+ * comparitor registers and interrupts not enabled until
+ * timer configures
+ */
+ /*
+ * XXX - configure delay -> amptimer_delay()
+ * XXX - configure cpu_initclocks -> amptimer_initclocks()
+ */
+ sc->sc_ticks_per_second = TIMER_FREQUENCY;
+
+ amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
+ amptimer_timecounter.tc_priv = sc;
+
+ tc_init(&amptimer_timecounter);
+}
+
+u_int
+amptimer_get_timecount(struct timecounter *tc)
+{
+ struct amptimer_softc *sc = amptimer_timecounter.tc_priv;
+ return bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
+}
+
+
+int
+amptimer_intr(void *frame)
+{
+ struct amptimer_softc *sc = amptimer_cd.cd_devs[0];
+ uint64_t now;
+ uint64_t nextevent;
+ uint32_t r, reg;
+ int skip = 1;
+ int rc = 0;
+
+ /*
+ * DSR - I know that the tick timer is 64 bits, but the following
+ * code deals with rollover, so there is no point in dealing
+ * with the 64 bit math, just let the 32 bit rollover
+ * do the right thing
+ */
+
+ now = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
+
+ while (sc->sc_nexttickevent <= now) {
+ sc->sc_nexttickevent += sc->sc_ticks_per_intr;
+ sc->sc_ticks_err_sum += sc->sc_ticks_err_cnt;
+ /* looping a few times is faster than divide */
+ while (sc->sc_ticks_err_sum > hz) {
+ sc->sc_nexttickevent += 1;
+ sc->sc_ticks_err_sum -= hz;
+ }
+
+ /* looping a few times is faster than divide */
+ while (sc->sc_ticks_err_sum > hz) {
+ sc->sc_nexttickevent += 1;
+ sc->sc_ticks_err_sum -= hz;
+ }
+
+ sc->sc_clk_count.ec_count++;
+ rc = 1;
+ hardclock(frame);
+ }
+ while (sc->sc_nextstatevent <= now) {
+ do {
+ r = random() & (sc->sc_statvar -1);
+ } while (r == 0); /* random == 0 not allowed */
+ sc->sc_nextstatevent += sc->sc_statmin + r;
+
+ /* XXX - correct nextstatevent? */
+ sc->sc_stat_count.ec_count++;
+ rc = 1;
+ statclock(frame);
+ }
+
+ if ((sc->sc_nexttickevent - now) < (sc->sc_nextstatevent - now))
+ nextevent = sc->sc_nexttickevent;
+ else
+ nextevent = sc->sc_nextstatevent;
+
+again:
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
+ reg &= ~GTIMER_CTRL_COMP;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
+ nextevent & 0xffffffff);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
+ nextevent >> 32);
+ reg |= GTIMER_CTRL_COMP;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
+
+ now = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
+ if (now >= nextevent) {
+ nextevent = now + skip;
+ skip += 1;
+ goto again;
+ }
+
+ return (rc);
+}
+
+void
+amptimer_cpu_initclocks()
+{
+ struct amptimer_softc *sc = amptimer_cd.cd_devs[0];
+ uint64_t next;
+ uint32_t reg;
+
+ stathz = 128;
+ profhz = 1024;
+
+ sc->sc_ticks_per_second = TIMER_FREQUENCY;
+
+ setstatclockrate(stathz);
+
+ sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz;
+ sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz;
+ sc->sc_ticks_err_sum = 0;;
+
+ /* establish interrupts */
+ /* XXX - irq */
+ arm_intr_establish(27, IPL_CLOCK, amptimer_intr,
+ NULL, "tick");
+
+ /* setup timer 0 (hardware timer 2) */
+ next = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
+ sc->sc_nexttickevent = sc->sc_nextstatevent = next;
+
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
+ reg &= ~GTIMER_CTRL_COMP;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
+ next & 0xffffffff);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
+ next >> 32);
+ reg |= GTIMER_CTRL_COMP | GTIMER_CTRL_IRQ;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
+
+}