/* * arch/arm/mach-orion/time.c * * Core time functions for Marvell Orion System On Chip * * Maintainer: Tzachi Perelstein * * 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 #include #include #include #include #include #include "common.h" /* * Timer0: clock_event_device, Tick. * Timer1: clocksource, Free running. * WatchDog: Not used. * * Timers are counting down. */ #define CLOCKEVENT 0 #define CLOCKSOURCE 1 /* * Timers bits */ #define BRIDGE_INT_TIMER(x) (1 << ((x) + 1)) #define TIMER_EN(x) (1 << ((x) * 2)) #define TIMER_RELOAD_EN(x) (1 << (((x) * 2) + 1)) #define BRIDGE_INT_TIMER_WD (1 << 3) #define TIMER_WD_EN (1 << 4) #define TIMER_WD_RELOAD_EN (1 << 5) static cycle_t orion_clksrc_read(void) { return (0xffffffff - orion_read(TIMER_VAL(CLOCKSOURCE))); } static struct clocksource orion_clksrc = { .name = "orion_clocksource", .shift = 20, .rating = 300, .read = orion_clksrc_read, .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static int orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev) { unsigned long flags; if (delta == 0) return -ETIME; local_irq_save(flags); /* * Clear and enable timer interrupt bit */ orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); /* * Setup new timer value */ orion_write(TIMER_VAL(CLOCKEVENT), delta); /* * Disable auto reload and kickoff the timer */ orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT)); orion_setbits(TIMER_CTRL, TIMER_EN(CLOCKEVENT)); local_irq_restore(flags); return 0; } static void orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) { unsigned long flags; local_irq_save(flags); if (mode == CLOCK_EVT_MODE_PERIODIC) { /* * Setup latch cycles in timer and enable reload interrupt. */ orion_write(TIMER_VAL_RELOAD(CLOCKEVENT), LATCH); orion_write(TIMER_VAL(CLOCKEVENT), LATCH); orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | TIMER_EN(CLOCKEVENT)); } else { /* * Disable timer and interrupt */ orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | TIMER_EN(CLOCKEVENT)); } local_irq_restore(flags); } static struct clock_event_device orion_clkevt = { .name = "orion_tick", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .shift = 32, .rating = 300, .cpumask = CPU_MASK_CPU0, .set_next_event = orion_clkevt_next_event, .set_mode = orion_clkevt_mode, }; static irqreturn_t orion_timer_interrupt(int irq, void *dev_id) { /* * Clear cause bit and do event */ orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); orion_clkevt.event_handler(&orion_clkevt); return IRQ_HANDLED; } static struct irqaction orion_timer_irq = { .name = "orion_tick", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = orion_timer_interrupt }; static void orion_timer_init(void) { /* * Setup clocksource free running timer (no interrupt on reload) */ orion_write(TIMER_VAL(CLOCKSOURCE), 0xffffffff); orion_write(TIMER_VAL_RELOAD(CLOCKSOURCE), 0xffffffff); orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKSOURCE)); orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKSOURCE) | TIMER_EN(CLOCKSOURCE)); /* * Register clocksource */ orion_clksrc.mult = clocksource_hz2mult(CLOCK_TICK_RATE, orion_clksrc.shift); clocksource_register(&orion_clksrc); /* * Connect and enable tick handler */ setup_irq(IRQ_ORION_BRIDGE, &orion_timer_irq); /* * Register clockevent */ orion_clkevt.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, orion_clkevt.shift); orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe, &orion_clkevt); orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt); clockevents_register_device(&orion_clkevt); } struct sys_timer orion_timer = { .init = orion_timer_init, };