summaryrefslogtreecommitdiff
path: root/arch/mn10300/kernel/cevt-mn10300.c
blob: 3aae9f5a98aac5c98d8858512e77fbdb1ce3d14d (plain)
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(data->affinity, 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;
}