[PATCH v2 3/5] ARM: SPMP8000: Add clocksource and clockevent drivers
Zoltan Devai
zoss at devai.org
Wed Oct 19 12:01:56 EDT 2011
This adds a clocksource and clockevent for the SPMP8000 machine.
Cc: John Stultz <johnstul at us.ibm.com>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Grant Likely <grant.likely at secretlab.ca>
Signed-off-by: Zoltan Devai <zoss at devai.org>
---
drivers/clocksource/Makefile | 3 +-
drivers/clocksource/spmp8000_tmrb.c | 159 +++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+), 1 deletions(-)
create mode 100644 drivers/clocksource/spmp8000_tmrb.c
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d81a1d..adb575e 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
-obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
\ No newline at end of file
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
+obj-$(CONFIG_ARCH_SPMP8000) += spmp8000_tmrb.o
diff --git a/drivers/clocksource/spmp8000_tmrb.c b/drivers/clocksource/spmp8000_tmrb.c
new file mode 100644
index 0000000..8c0f042
--- /dev/null
+++ b/drivers/clocksource/spmp8000_tmrb.c
@@ -0,0 +1,159 @@
+/*
+ * SPMP8000 machines timer functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss at devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <asm/mach/time.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+/* Timer constraints:
+ * - WDT: Can't clear the irq directly, only by resetting the whole counter
+ * in the ISR, which means that IRQs will jitter
+ * - Timer_B: Can't reset the timer value, so at start, the first IRQ
+ * will happen at some random time.
+ */
+#define SPMP8000_TMRB(tnum) (tnum * 0x20)
+#define SPMP8000_TMRB_CTR 0x00
+#define SPMP8000_TMRB_CTR_TE BIT(0)
+#define SPMP8000_TMRB_CTR_IE BIT(1)
+#define SPMP8000_TMRB_CTR_OE BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON BIT(3)
+#define SPMP8000_TMRB_CTR_UD BIT(4)
+#define SPMP8000_TMRB_CTR_UDS BIT(5)
+#define SPMP8000_TMRB_CTR_OM BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH 8
+#define SPMP8000_TMRB_CTR_M_SH 10
+#define SPMP8000_TMRB_PSR 0x04
+#define SPMP8000_TMRB_LDR 0x08
+#define SPMP8000_TMRB_VLR 0x08
+#define SPMP8000_TMRB_ISR 0x0C
+#define SPMP8000_TMRB_CMP 0x10
+
+static unsigned long clkrate;
+static const unsigned int tickrate = 1012500;
+
+#define CS_TIMER 2
+#define CE_TIMER 1
+static void __iomem *cs_base;
+static void __iomem *ce_base;
+
+static void tmrb_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ writel((tickrate / HZ), ce_base + SPMP8000_TMRB_LDR);
+ /* Configure as periodic, down counter, IEN, enable timer */
+ writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_IE |
+ (1 << SPMP8000_TMRB_CTR_M_SH),
+ ce_base + SPMP8000_TMRB_CTR);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ /* Disable timer */
+ writel(0, ce_base + SPMP8000_TMRB_CTR);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static struct clock_event_device tmrb1_clkevt = {
+ .name = "tmrb1",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+ .rating = 200,
+ .set_mode = tmrb_set_mode,
+};
+
+static irqreturn_t tmrb1_isr(int irq, void *dev_id)
+{
+ tmrb1_clkevt.event_handler(&tmrb1_clkevt);
+
+ /* Clear IRQ */
+ writel(0, ce_base + SPMP8000_TMRB_ISR);
+
+ return IRQ_HANDLED;
+};
+
+static struct irqaction tmrb1_irq = {
+ .name = "tmrb1_irq",
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = tmrb1_isr,
+};
+
+void __init spmp8000_sys_timer_init(void)
+{
+ struct device_node *np;
+ unsigned int irq;
+ void *tmrb_base;
+ const __be32 *prop;
+
+ np = of_find_compatible_node(NULL, NULL, "sunplus,spmp8000-timer");
+ if (!np)
+ panic("spmp8000: unable to find timer node in dtb\n");
+
+ tmrb_base = of_iomap(np, 0);
+ if (!tmrb_base)
+ panic("spmp8000: unable to map timer cpu registers\n");
+
+ irq = of_irq_to_resource(np, CE_TIMER, NULL);
+ if (irq == NO_IRQ)
+ panic("spmp8000: unable to get interrupts of timer\n");
+
+ prop = of_get_property(np, "clock-freq", NULL);
+ if (!prop)
+ panic("spmp8000: Can't get boot clock rate of timer\n");
+ clkrate = be32_to_cpup(prop);
+
+ of_node_put(np);
+
+ cs_base = tmrb_base + SPMP8000_TMRB(CS_TIMER);
+ ce_base = tmrb_base + SPMP8000_TMRB(CE_TIMER);
+
+ /* Clocksource */
+ /* Disable timer */
+ writel(0, cs_base + SPMP8000_TMRB_CTR);
+
+ /* Reset counter value
+ * Not really possible unless setting end-1 LDR value and waiting
+ * until the counter reaches that */
+
+ /* Prescale timer */
+ writel((clkrate / tickrate) - 1, cs_base + SPMP8000_TMRB_PSR);
+
+ /* Register the clocksource */
+ clocksource_mmio_init(cs_base + SPMP8000_TMRB_VLR, "tmrb2",
+ tickrate, 200, 16, clocksource_mmio_readl_up);
+
+ /* Configure as free running (0 - 0xFFFF), up counter, enable timer */
+ writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_UD,
+ cs_base + SPMP8000_TMRB_CTR);
+
+ /* Clockevent */
+ setup_irq(irq, &tmrb1_irq);
+
+ /* Disable timer */
+ writel(0, ce_base + SPMP8000_TMRB_CTR);
+
+ /* Prescale timer */
+ writel((clkrate / tickrate) - 1, ce_base + SPMP8000_TMRB_PSR);
+
+ clockevents_register_device(&tmrb1_clkevt);
+}
--
1.7.4.1
More information about the linux-arm-kernel
mailing list