Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /* MN10300 clockevents
*
* Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
* Written by Mark Salter (msalter@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/timex.h>
#include "internal.h"
#ifdef CONFIG_SMP
#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST)
#error "This doesn't scale well! Need per-core local timers."
#endif
#else /* CONFIG_SMP */
#define stop_jiffies_counter1()
#define reload_jiffies_counter1(x)
#define TMJC1IRQ TMJCIRQ
#endif
static int next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
if (cpu == 0) {
stop_jiffies_counter();
reload_jiffies_counter(delta - 1);
} else {
stop_jiffies_counter1();
reload_jiffies_counter1(delta - 1);
}
return 0;
}
static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device);
static DEFINE_PER_CPU(struct irqaction, timer_irq);
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *cd;
unsigned int cpu = smp_processor_id();
if (cpu == 0)
stop_jiffies_counter();
else
stop_jiffies_counter1();
cd = &per_cpu(mn10300_clockevent_device, cpu);
cd->event_handler(cd);
return IRQ_HANDLED;
}
static void event_handler(struct clock_event_device *dev)
{
}
static inline void setup_jiffies_interrupt(int irq,
struct irqaction *action)
{
u16 tmp;
setup_irq(irq, action);
set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL));
GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST;
tmp = GxICR(irq);
}
int __init init_clockevents(void)
{
struct clock_event_device *cd;
struct irqaction *iact;
unsigned int cpu = smp_processor_id();
cd = &per_cpu(mn10300_clockevent_device, cpu);
if (cpu == 0) {
stop_jiffies_counter();
cd->irq = TMJCIRQ;
} else {
stop_jiffies_counter1();
cd->irq = TMJC1IRQ;
}
cd->name = "Timestamp";
cd->features = CLOCK_EVT_FEAT_ONESHOT;
/* Calculate shift/mult. We want to spawn at least 1 second */
clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1);
/* Calculate the min / max delta */
cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd);
cd->min_delta_ns = clockevent_delta2ns(100, cd);
cd->rating = 200;
cd->cpumask = cpumask_of(smp_processor_id());
cd->event_handler = event_handler;
cd->set_next_event = next_event;
iact = &per_cpu(timer_irq, cpu);
iact->flags = IRQF_SHARED | IRQF_TIMER;
iact->handler = timer_interrupt;
clockevents_register_device(cd);
#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
/* setup timer irq affinity so it only runs on this cpu */
{
struct irq_data *data;
data = irq_get_irq_data(cd->irq);
cpumask_copy(irq_data_get_affinity_mask(data), cpumask_of(cpu));
iact->flags |= IRQF_NOBALANCING;
}
#endif
if (cpu == 0) {
reload_jiffies_counter(MN10300_JC_PER_HZ - 1);
iact->name = "CPU0 Timer";
} else {
reload_jiffies_counter1(MN10300_JC_PER_HZ - 1);
iact->name = "CPU1 Timer";
}
setup_jiffies_interrupt(cd->irq, iact);
return 0;
}
|