summaryrefslogtreecommitdiff
path: root/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c
diff options
context:
space:
mode:
authorFlorian Fainelli <florian@openwrt.org>2012-06-19 14:48:56 +0000
committerFlorian Fainelli <florian@openwrt.org>2012-06-19 14:48:56 +0000
commitf4afa00862b7db8a2da8c012843a286716c0a534 (patch)
tree9c15efab8fd398654de98648e07f26c51d6eec66 /target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c
parent7c87391c55a713a9ef9df169ab23e79177184b1a (diff)
downloadmtk-20170518-f4afa00862b7db8a2da8c012843a286716c0a534.zip
mtk-20170518-f4afa00862b7db8a2da8c012843a286716c0a534.tar.gz
mtk-20170518-f4afa00862b7db8a2da8c012843a286716c0a534.tar.bz2
add Moschip MSC814x support
This target currently only supports Moschip's MCS8140 SoC, but support for other chips in the same family (MCS8142, MCS8144) will be easy to add. Target support is entirely using Device Tree for probing peripherals. Drivers support include: - PCI - USB 1 & 2 - watchdog - random number generator - UART - timer - internal Ethernet PHY - Ethernet MAC core Support for the following boards is included using Device Tree - Devolo dLAN USB Extender - Tigal RBT-832 SVN-Revision: 32462
Diffstat (limited to 'target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c')
-rw-r--r--target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c b/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c
new file mode 100644
index 0000000..f515c0b
--- /dev/null
+++ b/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/timer.c
@@ -0,0 +1,133 @@
+/*
+ * Moschip MCS814x timer routines
+ *
+ * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
+ *
+ * Licensed under GPLv2
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/mach/time.h>
+#include <mach/hardware.h>
+
+/* Timer block registers */
+#define TIMER_VAL 0x00
+#define TIMER_CTL 0x04
+
+static u32 last_reload;
+static u32 timer_correct;
+static u32 clock_rate;
+static u32 timer_reload_value;
+static void __iomem *mcs814x_timer_base;
+
+static inline unsigned long ticks2usecs(u32 x)
+{
+ return x / (clock_rate / 1000000);
+}
+
+/*
+ * Returns number of ms since last clock interrupt. Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ */
+static unsigned long mcs814x_gettimeoffset(void)
+{
+ u32 ticks = __raw_readl(mcs814x_timer_base + TIMER_VAL);
+
+ if (ticks < last_reload)
+ return ticks2usecs(ticks + (u32)(0xffffffff - last_reload));
+ else
+ return ticks2usecs(ticks - last_reload);
+}
+
+
+static irqreturn_t mcs814x_timer_interrupt(int irq, void *dev_id)
+{
+ u32 count = __raw_readl(mcs814x_timer_base + TIMER_VAL);
+
+ /* take into account delay up to this moment */
+ last_reload = count + timer_correct + timer_reload_value;
+
+ if (last_reload < timer_reload_value) {
+ last_reload = timer_reload_value;
+ } else {
+ if (timer_correct == 0)
+ timer_correct = __raw_readl(mcs814x_timer_base + TIMER_VAL) - count;
+ }
+ __raw_writel(last_reload, mcs814x_timer_base + TIMER_VAL);
+
+ timer_tick();
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction mcs814x_timer_irq = {
+ .name = "mcs814x-timer",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = mcs814x_timer_interrupt,
+};
+
+static struct of_device_id mcs814x_timer_ids[] = {
+ { .compatible = "moschip,mcs814x-timer" },
+ { /* sentinel */ },
+};
+
+static void __init mcs814x_of_timer_init(void)
+{
+ struct device_node *np;
+ const unsigned int *intspec;
+
+ np = of_find_matching_node(NULL, mcs814x_timer_ids);
+ if (!np)
+ panic("unable to find compatible timer node in dtb");
+
+ mcs814x_timer_base = of_iomap(np, 0);
+ if (!mcs814x_timer_base)
+ panic("unable to remap timer cpu registers");
+
+ intspec = of_get_property(np, "interrupts", NULL);
+ if (!intspec)
+ panic("no interrupts property for timer");
+
+ mcs814x_timer_irq.irq = be32_to_cpup(intspec);
+}
+
+static void __init mcs814x_timer_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_get_sys("timer0", NULL);
+ if (IS_ERR_OR_NULL(clk))
+ panic("unable to get timer0 clock");
+
+ clock_rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ mcs814x_of_timer_init();
+
+ pr_info("Timer frequency: %d (kHz)\n", clock_rate / 1000);
+
+ timer_reload_value = 0xffffffff - (clock_rate / HZ);
+
+ /* disable timer */
+ __raw_writel(0, mcs814x_timer_base + TIMER_CTL);
+ __raw_writel(timer_reload_value, mcs814x_timer_base + TIMER_VAL);
+ last_reload = timer_reload_value;
+
+ setup_irq(mcs814x_timer_irq.irq, &mcs814x_timer_irq);
+ /* enable timer, stop timer in debug mode */
+ __raw_writel(0x03, mcs814x_timer_base + TIMER_CTL);
+}
+
+struct sys_timer mcs814x_timer = {
+ .init = mcs814x_timer_init,
+ .offset = mcs814x_gettimeoffset,
+};