aboutsummaryrefslogtreecommitdiff
path: root/drivers/clocksource
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/cadence_ttc_timer.c13
-rw-r--r--drivers/clocksource/moxart_timer.c164
-rw-r--r--drivers/clocksource/sun4i_timer.c110
-rw-r--r--drivers/clocksource/time-orion.c2
5 files changed, 256 insertions, 34 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8b00c5cebfa..704d6d342ad 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_ORION_TIMER) += time-orion.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
+obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o
obj-$(CONFIG_ARCH_MXS) += mxs_timer.o
obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index 4cbe28c7463..b2bb3a4bc20 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -21,7 +21,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
-#include <linux/clk-provider.h>
+#include <linux/sched_clock.h>
/*
* This driver configures the 2 16-bit count-up timers as follows:
@@ -95,6 +95,8 @@ struct ttc_timer_clockevent {
#define to_ttc_timer_clkevent(x) \
container_of(x, struct ttc_timer_clockevent, ce)
+static void __iomem *ttc_sched_clock_val_reg;
+
/**
* ttc_set_interval - Set the timer interval value
*
@@ -156,6 +158,11 @@ static cycle_t __ttc_clocksource_read(struct clocksource *cs)
TTC_COUNT_VAL_OFFSET);
}
+static u32 notrace ttc_sched_clock_read(void)
+{
+ return __raw_readl(ttc_sched_clock_val_reg);
+}
+
/**
* ttc_set_next_event - Sets the time interval for next event
*
@@ -297,6 +304,10 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
kfree(ttccs);
return;
}
+
+ ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
+ setup_sched_clock(ttc_sched_clock_read, 16,
+ clk_get_rate(ttccs->ttc.clk) / PRESCALE);
}
static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
new file mode 100644
index 00000000000..08a5943b3e4
--- /dev/null
+++ b/drivers/clocksource/moxart_timer.c
@@ -0,0 +1,164 @@
+/*
+ * MOXA ART SoCs timer handling.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * 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/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/clocksource.h>
+
+#define TIMER1_BASE 0x00
+#define TIMER2_BASE 0x10
+#define TIMER3_BASE 0x20
+
+#define REG_COUNT 0x0 /* writable */
+#define REG_LOAD 0x4
+#define REG_MATCH1 0x8
+#define REG_MATCH2 0xC
+
+#define TIMER_CR 0x30
+#define TIMER_INTR_STATE 0x34
+#define TIMER_INTR_MASK 0x38
+
+/*
+ * TIMER_CR flags:
+ *
+ * TIMEREG_CR_*_CLOCK 0: PCLK, 1: EXT1CLK
+ * TIMEREG_CR_*_INT overflow interrupt enable bit
+ */
+#define TIMEREG_CR_1_ENABLE BIT(0)
+#define TIMEREG_CR_1_CLOCK BIT(1)
+#define TIMEREG_CR_1_INT BIT(2)
+#define TIMEREG_CR_2_ENABLE BIT(3)
+#define TIMEREG_CR_2_CLOCK BIT(4)
+#define TIMEREG_CR_2_INT BIT(5)
+#define TIMEREG_CR_3_ENABLE BIT(6)
+#define TIMEREG_CR_3_CLOCK BIT(7)
+#define TIMEREG_CR_3_INT BIT(8)
+#define TIMEREG_CR_COUNT_UP BIT(9)
+
+#define TIMER1_ENABLE (TIMEREG_CR_2_ENABLE | TIMEREG_CR_1_ENABLE)
+#define TIMER1_DISABLE (TIMEREG_CR_2_ENABLE)
+
+static void __iomem *base;
+static unsigned int clock_count_per_tick;
+
+static void moxart_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_RESUME:
+ case CLOCK_EVT_MODE_ONESHOT:
+ writel(TIMER1_DISABLE, base + TIMER_CR);
+ writel(~0, base + TIMER1_BASE + REG_LOAD);
+ break;
+ case CLOCK_EVT_MODE_PERIODIC:
+ writel(clock_count_per_tick, base + TIMER1_BASE + REG_LOAD);
+ writel(TIMER1_ENABLE, base + TIMER_CR);
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ writel(TIMER1_DISABLE, base + TIMER_CR);
+ break;
+ }
+}
+
+static int moxart_clkevt_next_event(unsigned long cycles,
+ struct clock_event_device *unused)
+{
+ u32 u;
+
+ writel(TIMER1_DISABLE, base + TIMER_CR);
+
+ u = readl(base + TIMER1_BASE + REG_COUNT) - cycles;
+ writel(u, base + TIMER1_BASE + REG_MATCH1);
+
+ writel(TIMER1_ENABLE, base + TIMER_CR);
+
+ return 0;
+}
+
+static struct clock_event_device moxart_clockevent = {
+ .name = "moxart_timer",
+ .rating = 200,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = moxart_clkevt_mode,
+ .set_next_event = moxart_clkevt_next_event,
+};
+
+static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction moxart_timer_irq = {
+ .name = "moxart-timer",
+ .flags = IRQF_TIMER,
+ .handler = moxart_timer_interrupt,
+ .dev_id = &moxart_clockevent,
+};
+
+static void __init moxart_timer_init(struct device_node *node)
+{
+ int ret, irq;
+ unsigned long pclk;
+ struct clk *clk;
+
+ base = of_iomap(node, 0);
+ if (!base)
+ panic("%s: of_iomap failed\n", node->full_name);
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (irq <= 0)
+ panic("%s: irq_of_parse_and_map failed\n", node->full_name);
+
+ ret = setup_irq(irq, &moxart_timer_irq);
+ if (ret)
+ panic("%s: setup_irq failed\n", node->full_name);
+
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk))
+ panic("%s: of_clk_get failed\n", node->full_name);
+
+ pclk = clk_get_rate(clk);
+
+ if (clocksource_mmio_init(base + TIMER2_BASE + REG_COUNT,
+ "moxart_timer", pclk, 200, 32,
+ clocksource_mmio_readl_down))
+ panic("%s: clocksource_mmio_init failed\n", node->full_name);
+
+ clock_count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);
+
+ writel(~0, base + TIMER2_BASE + REG_LOAD);
+ writel(TIMEREG_CR_2_ENABLE, base + TIMER_CR);
+
+ moxart_clockevent.cpumask = cpumask_of(0);
+ moxart_clockevent.irq = irq;
+
+ /*
+ * documentation is not publicly available:
+ * min_delta / max_delta obtained by trial-and-error,
+ * max_delta 0xfffffffe should be ok because count
+ * register size is u32
+ */
+ clockevents_config_and_register(&moxart_clockevent, pclk,
+ 0x4, 0xfffffffe);
+}
+CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index d4674e78ef3..8ead0258740 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -19,42 +19,83 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TIMER_IRQ_EN_REG 0x00
-#define TIMER_IRQ_EN(val) (1 << val)
+#define TIMER_IRQ_EN(val) BIT(val)
#define TIMER_IRQ_ST_REG 0x04
#define TIMER_CTL_REG(val) (0x10 * val + 0x10)
-#define TIMER_CTL_ENABLE (1 << 0)
-#define TIMER_CTL_AUTORELOAD (1 << 1)
-#define TIMER_CTL_ONESHOT (1 << 7)
-#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14)
-#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18)
-
-#define TIMER_SCAL 16
+#define TIMER_CTL_ENABLE BIT(0)
+#define TIMER_CTL_RELOAD BIT(1)
+#define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2)
+#define TIMER_CTL_CLK_SRC_OSC24M (1)
+#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
+#define TIMER_CTL_ONESHOT BIT(7)
+#define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14)
+#define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18)
static void __iomem *timer_base;
+static u32 ticks_per_jiffy;
+
+/*
+ * When we disable a timer, we need to wait at least for 2 cycles of
+ * the timer source clock. We will use for that the clocksource timer
+ * that is already setup and runs at the same frequency than the other
+ * timers, and we never will be disabled.
+ */
+static void sun4i_clkevt_sync(void)
+{
+ u32 old = readl(timer_base + TIMER_CNTVAL_REG(1));
+
+ while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < 3)
+ cpu_relax();
+}
+
+static void sun4i_clkevt_time_stop(u8 timer)
+{
+ u32 val = readl(timer_base + TIMER_CTL_REG(timer));
+ writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer));
+ sun4i_clkevt_sync();
+}
+
+static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay)
+{
+ writel(delay, timer_base + TIMER_INTVAL_REG(timer));
+}
+
+static void sun4i_clkevt_time_start(u8 timer, bool periodic)
+{
+ u32 val = readl(timer_base + TIMER_CTL_REG(timer));
+
+ if (periodic)
+ val &= ~TIMER_CTL_ONESHOT;
+ else
+ val |= TIMER_CTL_ONESHOT;
+
+ writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
+ timer_base + TIMER_CTL_REG(timer));
+}
static void sun4i_clkevt_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
{
- u32 u = readl(timer_base + TIMER_CTL_REG(0));
-
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- u &= ~(TIMER_CTL_ONESHOT);
- writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0));
+ sun4i_clkevt_time_stop(0);
+ sun4i_clkevt_time_setup(0, ticks_per_jiffy);
+ sun4i_clkevt_time_start(0, true);
break;
-
case CLOCK_EVT_MODE_ONESHOT:
- writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0));
+ sun4i_clkevt_time_stop(0);
+ sun4i_clkevt_time_start(0, false);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
default:
- writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0));
+ sun4i_clkevt_time_stop(0);
break;
}
}
@@ -62,10 +103,9 @@ static void sun4i_clkevt_mode(enum clock_event_mode mode,
static int sun4i_clkevt_next_event(unsigned long evt,
struct clock_event_device *unused)
{
- u32 u = readl(timer_base + TIMER_CTL_REG(0));
- writel(evt, timer_base + TIMER_CNTVAL_REG(0));
- writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD,
- timer_base + TIMER_CTL_REG(0));
+ sun4i_clkevt_time_stop(0);
+ sun4i_clkevt_time_setup(0, evt);
+ sun4i_clkevt_time_start(0, false);
return 0;
}
@@ -96,6 +136,11 @@ static struct irqaction sun4i_timer_irq = {
.dev_id = &sun4i_clockevent,
};
+static u32 sun4i_timer_sched_read(void)
+{
+ return ~readl(timer_base + TIMER_CNTVAL_REG(1));
+}
+
static void __init sun4i_timer_init(struct device_node *node)
{
unsigned long rate = 0;
@@ -114,22 +159,23 @@ static void __init sun4i_timer_init(struct device_node *node)
clk = of_clk_get(node, 0);
if (IS_ERR(clk))
panic("Can't get timer clock");
+ clk_prepare_enable(clk);
rate = clk_get_rate(clk);
- writel(rate / (TIMER_SCAL * HZ),
- timer_base + TIMER_INTVAL_REG(0));
+ writel(~0, timer_base + TIMER_INTVAL_REG(1));
+ writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
+ TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
+ timer_base + TIMER_CTL_REG(1));
+
+ setup_sched_clock(sun4i_timer_sched_read, 32, rate);
+ clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name,
+ rate, 300, 32, clocksource_mmio_readl_down);
- /* set clock source to HOSC, 16 pre-division */
- val = readl(timer_base + TIMER_CTL_REG(0));
- val &= ~(0x07 << 4);
- val &= ~(0x03 << 2);
- val |= (4 << 4) | (1 << 2);
- writel(val, timer_base + TIMER_CTL_REG(0));
+ ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
- /* set mode to auto reload */
- val = readl(timer_base + TIMER_CTL_REG(0));
- writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0));
+ writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
+ timer_base + TIMER_CTL_REG(0));
ret = setup_irq(irq, &sun4i_timer_irq);
if (ret)
@@ -141,8 +187,8 @@ static void __init sun4i_timer_init(struct device_node *node)
sun4i_clockevent.cpumask = cpumask_of(0);
- clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL,
- 0x1, 0xff);
+ clockevents_config_and_register(&sun4i_clockevent, rate, 0x1,
+ 0xffffffff);
}
CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
sun4i_timer_init);
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
index ecbeb681021..9c7f018a67c 100644
--- a/drivers/clocksource/time-orion.c
+++ b/drivers/clocksource/time-orion.c
@@ -19,7 +19,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
#define TIMER_CTRL 0x00
#define TIMER0_EN BIT(0)