linux/arch/microblaze/kernel/timer.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
   3 * Copyright (C) 2007-2009 PetaLogix
   4 * Copyright (C) 2006 Atmark Techno, Inc.
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License. See the file "COPYING" in the main directory of this archive
   8 * for more details.
   9 */
  10
  11#include <linux/init.h>
  12#include <linux/kernel.h>
  13#include <linux/param.h>
  14#include <linux/interrupt.h>
  15#include <linux/profile.h>
  16#include <linux/irq.h>
  17#include <linux/delay.h>
  18#include <linux/sched.h>
  19#include <linux/spinlock.h>
  20#include <linux/err.h>
  21#include <linux/clk.h>
  22#include <linux/clocksource.h>
  23#include <linux/clockchips.h>
  24#include <linux/io.h>
  25#include <linux/bug.h>
  26#include <asm/cpuinfo.h>
  27#include <asm/setup.h>
  28#include <asm/prom.h>
  29#include <asm/irq.h>
  30#include <asm/system.h>
  31#include <linux/cnt32_to_63.h>
  32
  33#ifdef CONFIG_SELFMOD_TIMER
  34#include <asm/selfmod.h>
  35#define TIMER_BASE      BARRIER_BASE_ADDR
  36#else
  37static unsigned int timer_baseaddr;
  38#define TIMER_BASE      timer_baseaddr
  39#endif
  40
  41static unsigned int freq_div_hz;
  42static unsigned int timer_clock_freq;
  43
  44#define TCSR0   (0x00)
  45#define TLR0    (0x04)
  46#define TCR0    (0x08)
  47#define TCSR1   (0x10)
  48#define TLR1    (0x14)
  49#define TCR1    (0x18)
  50
  51#define TCSR_MDT        (1<<0)
  52#define TCSR_UDT        (1<<1)
  53#define TCSR_GENT       (1<<2)
  54#define TCSR_CAPT       (1<<3)
  55#define TCSR_ARHT       (1<<4)
  56#define TCSR_LOAD       (1<<5)
  57#define TCSR_ENIT       (1<<6)
  58#define TCSR_ENT        (1<<7)
  59#define TCSR_TINT       (1<<8)
  60#define TCSR_PWMA       (1<<9)
  61#define TCSR_ENALL      (1<<10)
  62
  63static inline void microblaze_timer0_stop(void)
  64{
  65        out_be32(TIMER_BASE + TCSR0, in_be32(TIMER_BASE + TCSR0) & ~TCSR_ENT);
  66}
  67
  68static inline void microblaze_timer0_start_periodic(unsigned long load_val)
  69{
  70        if (!load_val)
  71                load_val = 1;
  72        out_be32(TIMER_BASE + TLR0, load_val); /* loading value to timer reg */
  73
  74        /* load the initial value */
  75        out_be32(TIMER_BASE + TCSR0, TCSR_LOAD);
  76
  77        /* see timer data sheet for detail
  78         * !ENALL - don't enable 'em all
  79         * !PWMA - disable pwm
  80         * TINT - clear interrupt status
  81         * ENT- enable timer itself
  82         * EINT - enable interrupt
  83         * !LOAD - clear the bit to let go
  84         * ARHT - auto reload
  85         * !CAPT - no external trigger
  86         * !GENT - no external signal
  87         * UDT - set the timer as down counter
  88         * !MDT0 - generate mode
  89         */
  90        out_be32(TIMER_BASE + TCSR0,
  91                        TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT);
  92}
  93
  94static inline void microblaze_timer0_start_oneshot(unsigned long load_val)
  95{
  96        if (!load_val)
  97                load_val = 1;
  98        out_be32(TIMER_BASE + TLR0, load_val); /* loading value to timer reg */
  99
 100        /* load the initial value */
 101        out_be32(TIMER_BASE + TCSR0, TCSR_LOAD);
 102
 103        out_be32(TIMER_BASE + TCSR0,
 104                        TCSR_TINT|TCSR_ENIT|TCSR_ENT|TCSR_ARHT|TCSR_UDT);
 105}
 106
 107static int microblaze_timer_set_next_event(unsigned long delta,
 108                                        struct clock_event_device *dev)
 109{
 110        pr_debug("%s: next event, delta %x\n", __func__, (u32)delta);
 111        microblaze_timer0_start_oneshot(delta);
 112        return 0;
 113}
 114
 115static void microblaze_timer_set_mode(enum clock_event_mode mode,
 116                                struct clock_event_device *evt)
 117{
 118        switch (mode) {
 119        case CLOCK_EVT_MODE_PERIODIC:
 120                printk(KERN_INFO "%s: periodic\n", __func__);
 121                microblaze_timer0_start_periodic(freq_div_hz);
 122                break;
 123        case CLOCK_EVT_MODE_ONESHOT:
 124                printk(KERN_INFO "%s: oneshot\n", __func__);
 125                break;
 126        case CLOCK_EVT_MODE_UNUSED:
 127                printk(KERN_INFO "%s: unused\n", __func__);
 128                break;
 129        case CLOCK_EVT_MODE_SHUTDOWN:
 130                printk(KERN_INFO "%s: shutdown\n", __func__);
 131                microblaze_timer0_stop();
 132                break;
 133        case CLOCK_EVT_MODE_RESUME:
 134                printk(KERN_INFO "%s: resume\n", __func__);
 135                break;
 136        }
 137}
 138
 139static struct clock_event_device clockevent_microblaze_timer = {
 140        .name           = "microblaze_clockevent",
 141        .features       = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
 142        .shift          = 8,
 143        .rating         = 300,
 144        .set_next_event = microblaze_timer_set_next_event,
 145        .set_mode       = microblaze_timer_set_mode,
 146};
 147
 148static inline void timer_ack(void)
 149{
 150        out_be32(TIMER_BASE + TCSR0, in_be32(TIMER_BASE + TCSR0));
 151}
 152
 153static irqreturn_t timer_interrupt(int irq, void *dev_id)
 154{
 155        struct clock_event_device *evt = &clockevent_microblaze_timer;
 156#ifdef CONFIG_HEART_BEAT
 157        heartbeat();
 158#endif
 159        timer_ack();
 160        evt->event_handler(evt);
 161        return IRQ_HANDLED;
 162}
 163
 164static struct irqaction timer_irqaction = {
 165        .handler = timer_interrupt,
 166        .flags = IRQF_DISABLED | IRQF_TIMER,
 167        .name = "timer",
 168        .dev_id = &clockevent_microblaze_timer,
 169};
 170
 171static __init void microblaze_clockevent_init(void)
 172{
 173        clockevent_microblaze_timer.mult =
 174                div_sc(timer_clock_freq, NSEC_PER_SEC,
 175                                clockevent_microblaze_timer.shift);
 176        clockevent_microblaze_timer.max_delta_ns =
 177                clockevent_delta2ns((u32)~0, &clockevent_microblaze_timer);
 178        clockevent_microblaze_timer.min_delta_ns =
 179                clockevent_delta2ns(1, &clockevent_microblaze_timer);
 180        clockevent_microblaze_timer.cpumask = cpumask_of(0);
 181        clockevents_register_device(&clockevent_microblaze_timer);
 182}
 183
 184static cycle_t microblaze_read(struct clocksource *cs)
 185{
 186        /* reading actual value of timer 1 */
 187        return (cycle_t) (in_be32(TIMER_BASE + TCR1));
 188}
 189
 190static struct timecounter microblaze_tc = {
 191        .cc = NULL,
 192};
 193
 194static cycle_t microblaze_cc_read(const struct cyclecounter *cc)
 195{
 196        return microblaze_read(NULL);
 197}
 198
 199static struct cyclecounter microblaze_cc = {
 200        .read = microblaze_cc_read,
 201        .mask = CLOCKSOURCE_MASK(32),
 202        .shift = 8,
 203};
 204
 205static int __init init_microblaze_timecounter(void)
 206{
 207        microblaze_cc.mult = div_sc(timer_clock_freq, NSEC_PER_SEC,
 208                                microblaze_cc.shift);
 209
 210        timecounter_init(&microblaze_tc, &microblaze_cc, sched_clock());
 211
 212        return 0;
 213}
 214
 215static struct clocksource clocksource_microblaze = {
 216        .name           = "microblaze_clocksource",
 217        .rating         = 300,
 218        .read           = microblaze_read,
 219        .mask           = CLOCKSOURCE_MASK(32),
 220        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 221};
 222
 223static int __init microblaze_clocksource_init(void)
 224{
 225        if (clocksource_register_hz(&clocksource_microblaze, timer_clock_freq))
 226                panic("failed to register clocksource");
 227
 228        /* stop timer1 */
 229        out_be32(TIMER_BASE + TCSR1, in_be32(TIMER_BASE + TCSR1) & ~TCSR_ENT);
 230        /* start timer1 - up counting without interrupt */
 231        out_be32(TIMER_BASE + TCSR1, TCSR_TINT|TCSR_ENT|TCSR_ARHT);
 232
 233        /* register timecounter - for ftrace support */
 234        init_microblaze_timecounter();
 235        return 0;
 236}
 237
 238/*
 239 * We have to protect accesses before timer initialization
 240 * and return 0 for sched_clock function below.
 241 */
 242static int timer_initialized;
 243
 244void __init time_init(void)
 245{
 246        u32 irq, i = 0;
 247        u32 timer_num = 1;
 248        struct device_node *timer = NULL;
 249        const void *prop;
 250#ifdef CONFIG_SELFMOD_TIMER
 251        unsigned int timer_baseaddr = 0;
 252        int arr_func[] = {
 253                                (int)&microblaze_read,
 254                                (int)&timer_interrupt,
 255                                (int)&microblaze_clocksource_init,
 256                                (int)&microblaze_timer_set_mode,
 257                                (int)&microblaze_timer_set_next_event,
 258                                0
 259                        };
 260#endif
 261        const char * const timer_list[] = {
 262                "xlnx,xps-timer-1.00.a",
 263                NULL
 264        };
 265
 266        for (i = 0; timer_list[i] != NULL; i++) {
 267                timer = of_find_compatible_node(NULL, NULL, timer_list[i]);
 268                if (timer)
 269                        break;
 270        }
 271        BUG_ON(!timer);
 272
 273        timer_baseaddr = be32_to_cpup(of_get_property(timer, "reg", NULL));
 274        timer_baseaddr = (unsigned long) ioremap(timer_baseaddr, PAGE_SIZE);
 275        irq = be32_to_cpup(of_get_property(timer, "interrupts", NULL));
 276        timer_num = be32_to_cpup(of_get_property(timer,
 277                                                "xlnx,one-timer-only", NULL));
 278        if (timer_num) {
 279                eprintk(KERN_EMERG "Please enable two timers in HW\n");
 280                BUG();
 281        }
 282
 283#ifdef CONFIG_SELFMOD_TIMER
 284        selfmod_function((int *) arr_func, timer_baseaddr);
 285#endif
 286        printk(KERN_INFO "%s #0 at 0x%08x, irq=%d\n",
 287                timer_list[i], timer_baseaddr, irq);
 288
 289        /* If there is clock-frequency property than use it */
 290        prop = of_get_property(timer, "clock-frequency", NULL);
 291        if (prop)
 292                timer_clock_freq = be32_to_cpup(prop);
 293        else
 294                timer_clock_freq = cpuinfo.cpu_clock_freq;
 295
 296        freq_div_hz = timer_clock_freq / HZ;
 297
 298        setup_irq(irq, &timer_irqaction);
 299#ifdef CONFIG_HEART_BEAT
 300        setup_heartbeat();
 301#endif
 302        microblaze_clocksource_init();
 303        microblaze_clockevent_init();
 304        timer_initialized = 1;
 305}
 306
 307unsigned long long notrace sched_clock(void)
 308{
 309        if (timer_initialized) {
 310                struct clocksource *cs = &clocksource_microblaze;
 311
 312                cycle_t cyc = cnt32_to_63(cs->read(NULL)) & LLONG_MAX;
 313                return clocksource_cyc2ns(cyc, cs->mult, cs->shift);
 314        }
 315        return 0;
 316}
 317
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.