linux/arch/unicore32/kernel/time.c
<<
>>
Prefs
   1/*
   2 * linux/arch/unicore32/kernel/time.c
   3 *
   4 * Code specific to PKUnity SoC and UniCore ISA
   5 *
   6 *      Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
   7 *      Copyright (C) 2001-2010 Guan Xuetao
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13#include <linux/init.h>
  14#include <linux/errno.h>
  15#include <linux/interrupt.h>
  16#include <linux/irq.h>
  17#include <linux/timex.h>
  18#include <linux/clockchips.h>
  19
  20#include <mach/hardware.h>
  21
  22#define MIN_OSCR_DELTA 2
  23
  24static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id)
  25{
  26        struct clock_event_device *c = dev_id;
  27
  28        /* Disarm the compare/match, signal the event. */
  29        writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
  30        writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
  31        c->event_handler(c);
  32
  33        return IRQ_HANDLED;
  34}
  35
  36static int
  37puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c)
  38{
  39        unsigned long next, oscr;
  40
  41        writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER);
  42        next = readl(OST_OSCR) + delta;
  43        writel(next, OST_OSMR0);
  44        oscr = readl(OST_OSCR);
  45
  46        return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
  47}
  48
  49static void
  50puv3_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c)
  51{
  52        switch (mode) {
  53        case CLOCK_EVT_MODE_ONESHOT:
  54        case CLOCK_EVT_MODE_UNUSED:
  55        case CLOCK_EVT_MODE_SHUTDOWN:
  56                writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
  57                writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
  58                break;
  59
  60        case CLOCK_EVT_MODE_RESUME:
  61        case CLOCK_EVT_MODE_PERIODIC:
  62                break;
  63        }
  64}
  65
  66static struct clock_event_device ckevt_puv3_osmr0 = {
  67        .name           = "osmr0",
  68        .features       = CLOCK_EVT_FEAT_ONESHOT,
  69        .rating         = 200,
  70        .set_next_event = puv3_osmr0_set_next_event,
  71        .set_mode       = puv3_osmr0_set_mode,
  72};
  73
  74static cycle_t puv3_read_oscr(struct clocksource *cs)
  75{
  76        return readl(OST_OSCR);
  77}
  78
  79static struct clocksource cksrc_puv3_oscr = {
  80        .name           = "oscr",
  81        .rating         = 200,
  82        .read           = puv3_read_oscr,
  83        .mask           = CLOCKSOURCE_MASK(32),
  84        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  85};
  86
  87static struct irqaction puv3_timer_irq = {
  88        .name           = "ost0",
  89        .flags          = IRQF_TIMER | IRQF_IRQPOLL,
  90        .handler        = puv3_ost0_interrupt,
  91        .dev_id         = &ckevt_puv3_osmr0,
  92};
  93
  94void __init time_init(void)
  95{
  96        writel(0, OST_OIER);            /* disable any timer interrupts */
  97        writel(0, OST_OSSR);            /* clear status on all timers */
  98
  99        clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5);
 100
 101        ckevt_puv3_osmr0.max_delta_ns =
 102                clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0);
 103        ckevt_puv3_osmr0.min_delta_ns =
 104                clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1;
 105        ckevt_puv3_osmr0.cpumask = cpumask_of(0);
 106
 107        setup_irq(IRQ_TIMER0, &puv3_timer_irq);
 108
 109        clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE);
 110        clockevents_register_device(&ckevt_puv3_osmr0);
 111}
 112
 113#ifdef CONFIG_PM
 114unsigned long osmr[4], oier;
 115
 116void puv3_timer_suspend(void)
 117{
 118        osmr[0] = readl(OST_OSMR0);
 119        osmr[1] = readl(OST_OSMR1);
 120        osmr[2] = readl(OST_OSMR2);
 121        osmr[3] = readl(OST_OSMR3);
 122        oier = readl(OST_OIER);
 123}
 124
 125void puv3_timer_resume(void)
 126{
 127        writel(0, OST_OSSR);
 128        writel(osmr[0], OST_OSMR0);
 129        writel(osmr[1], OST_OSMR1);
 130        writel(osmr[2], OST_OSMR2);
 131        writel(osmr[3], OST_OSMR3);
 132        writel(oier, OST_OIER);
 133
 134        /*
 135         * OSMR0 is the system timer: make sure OSCR is sufficiently behind
 136         */
 137        writel(readl(OST_OSMR0) - LATCH, OST_OSCR);
 138}
 139#else
 140void puv3_timer_suspend(void) { };
 141void puv3_timer_resume(void) { };
 142#endif
 143
 144