|
@@ -21,25 +21,80 @@
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/irq.h>
|
|
|
#include <linux/clocksource.h>
|
|
|
+#include <linux/clockchips.h>
|
|
|
#include <linux/io.h>
|
|
|
|
|
|
#include <mach/hardware.h>
|
|
|
#include <asm/mach/time.h>
|
|
|
#include <mach/netx-regs.h>
|
|
|
|
|
|
+#define TIMER_CLOCKEVENT 0
|
|
|
#define TIMER_CLOCKSOURCE 1
|
|
|
|
|
|
+static void netx_set_mode(enum clock_event_mode mode,
|
|
|
+ struct clock_event_device *clk)
|
|
|
+{
|
|
|
+ u32 tmode;
|
|
|
+
|
|
|
+ /* disable timer */
|
|
|
+ writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
|
|
|
+
|
|
|
+ switch (mode) {
|
|
|
+ case CLOCK_EVT_MODE_PERIODIC:
|
|
|
+ writel(LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
|
|
|
+ tmode = NETX_GPIO_COUNTER_CTRL_RST_EN |
|
|
|
+ NETX_GPIO_COUNTER_CTRL_IRQ_EN |
|
|
|
+ NETX_GPIO_COUNTER_CTRL_RUN;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLOCK_EVT_MODE_ONESHOT:
|
|
|
+ writel(0, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
|
|
|
+ tmode = NETX_GPIO_COUNTER_CTRL_IRQ_EN |
|
|
|
+ NETX_GPIO_COUNTER_CTRL_RUN;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ WARN(1, "%s: unhandled mode %d\n", __func__, mode);
|
|
|
+ /* fall through */
|
|
|
+
|
|
|
+ case CLOCK_EVT_MODE_SHUTDOWN:
|
|
|
+ case CLOCK_EVT_MODE_UNUSED:
|
|
|
+ case CLOCK_EVT_MODE_RESUME:
|
|
|
+ tmode = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(tmode, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
|
|
|
+}
|
|
|
+
|
|
|
+static int netx_set_next_event(unsigned long evt,
|
|
|
+ struct clock_event_device *clk)
|
|
|
+{
|
|
|
+ writel(0 - evt, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKEVENT));
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct clock_event_device netx_clockevent = {
|
|
|
+ .name = "netx-timer" __stringify(TIMER_CLOCKEVENT),
|
|
|
+ .shift = 32,
|
|
|
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
|
+ .set_next_event = netx_set_next_event,
|
|
|
+ .set_mode = netx_set_mode,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* IRQ handler for the timer
|
|
|
*/
|
|
|
static irqreturn_t
|
|
|
netx_timer_interrupt(int irq, void *dev_id)
|
|
|
{
|
|
|
- timer_tick();
|
|
|
+ struct clock_event_device *evt = &netx_clockevent;
|
|
|
|
|
|
/* acknowledge interrupt */
|
|
|
writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
|
|
|
|
|
|
+ evt->event_handler(evt);
|
|
|
+
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
@@ -99,6 +154,17 @@ static void __init netx_timer_init(void)
|
|
|
clocksource_netx.mult =
|
|
|
clocksource_hz2mult(CLOCK_TICK_RATE, clocksource_netx.shift);
|
|
|
clocksource_register(&clocksource_netx);
|
|
|
+
|
|
|
+ netx_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
|
|
|
+ netx_clockevent.shift);
|
|
|
+ netx_clockevent.max_delta_ns =
|
|
|
+ clockevent_delta2ns(0xfffffffe, &netx_clockevent);
|
|
|
+ /* with max_delta_ns >= delta2ns(0x800) the system currently runs fine.
|
|
|
+ * Adding some safety ... */
|
|
|
+ netx_clockevent.min_delta_ns =
|
|
|
+ clockevent_delta2ns(0xa00, &netx_clockevent);
|
|
|
+ netx_clockevent.cpumask = cpumask_of_cpu(0);
|
|
|
+ clockevents_register_device(&netx_clockevent);
|
|
|
}
|
|
|
|
|
|
struct sys_timer netx_timer = {
|