diff options
Diffstat (limited to 'arch/arm/mach-sa1100/time.c')
-rw-r--r-- | arch/arm/mach-sa1100/time.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c new file mode 100644 index 00000000..6af26e8d --- /dev/null +++ b/arch/arm/mach-sa1100/time.c @@ -0,0 +1,143 @@ +/* + * linux/arch/arm/mach-sa1100/time.c + * + * Copyright (C) 1998 Deborah Wallach. + * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com> + * + * 2000/03/29 (C) Nicolas Pitre <nico@fluxnic.net> + * Rewritten: big cleanup, much simpler, better HZ accuracy. + * + */ +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/timex.h> +#include <linux/clockchips.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> +#include <mach/hardware.h> +#include <mach/irqs.h> + +static u32 notrace sa1100_read_sched_clock(void) +{ + return OSCR; +} + +#define MIN_OSCR_DELTA 2 + +static irqreturn_t sa1100_ost0_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *c = dev_id; + + /* Disarm the compare/match, signal the event. */ + OIER &= ~OIER_E0; + OSSR = OSSR_M0; + c->event_handler(c); + + return IRQ_HANDLED; +} + +static int +sa1100_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) +{ + unsigned long next, oscr; + + OIER |= OIER_E0; + next = OSCR + delta; + OSMR0 = next; + oscr = OSCR; + + return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; +} + +static void +sa1100_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) +{ + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + OIER &= ~OIER_E0; + OSSR = OSSR_M0; + break; + + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_PERIODIC: + break; + } +} + +static struct clock_event_device ckevt_sa1100_osmr0 = { + .name = "osmr0", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = sa1100_osmr0_set_next_event, + .set_mode = sa1100_osmr0_set_mode, +}; + +static struct irqaction sa1100_timer_irq = { + .name = "ost0", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = sa1100_ost0_interrupt, + .dev_id = &ckevt_sa1100_osmr0, +}; + +static void __init sa1100_timer_init(void) +{ + OIER = 0; + OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3; + + setup_sched_clock(sa1100_read_sched_clock, 32, 3686400); + + clockevents_calc_mult_shift(&ckevt_sa1100_osmr0, 3686400, 4); + ckevt_sa1100_osmr0.max_delta_ns = + clockevent_delta2ns(0x7fffffff, &ckevt_sa1100_osmr0); + ckevt_sa1100_osmr0.min_delta_ns = + clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_sa1100_osmr0) + 1; + ckevt_sa1100_osmr0.cpumask = cpumask_of(0); + + setup_irq(IRQ_OST0, &sa1100_timer_irq); + + clocksource_mmio_init(&OSCR, "oscr", CLOCK_TICK_RATE, 200, 32, + clocksource_mmio_readl_up); + clockevents_register_device(&ckevt_sa1100_osmr0); +} + +#ifdef CONFIG_PM +unsigned long osmr[4], oier; + +static void sa1100_timer_suspend(void) +{ + osmr[0] = OSMR0; + osmr[1] = OSMR1; + osmr[2] = OSMR2; + osmr[3] = OSMR3; + oier = OIER; +} + +static void sa1100_timer_resume(void) +{ + OSSR = 0x0f; + OSMR0 = osmr[0]; + OSMR1 = osmr[1]; + OSMR2 = osmr[2]; + OSMR3 = osmr[3]; + OIER = oier; + + /* + * OSMR0 is the system timer: make sure OSCR is sufficiently behind + */ + OSCR = OSMR0 - LATCH; +} +#else +#define sa1100_timer_suspend NULL +#define sa1100_timer_resume NULL +#endif + +struct sys_timer sa1100_timer = { + .init = sa1100_timer_init, + .suspend = sa1100_timer_suspend, + .resume = sa1100_timer_resume, +}; |