Blame view

arch/mips/kernel/cevt-r4k.c 5 KB
42f77542f   Ralf Baechle   [MIPS] time: Move...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file "COPYING" in the main directory of this archive
   * for more details.
   *
   * Copyright (C) 2007 MIPS Technologies, Inc.
   * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
   */
  #include <linux/clockchips.h>
  #include <linux/interrupt.h>
  #include <linux/percpu.h>
631330f58   Ralf Baechle   MIPS: Build fix -...
12
  #include <linux/smp.h>
ca4d3e674   David Howells   MIPS: Add missing...
13
  #include <linux/irq.h>
42f77542f   Ralf Baechle   [MIPS] time: Move...
14

f887b93e1   Ralf Baechle   [MIPS] SMTC: Buil...
15
  #include <asm/smtc_ipi.h>
42f77542f   Ralf Baechle   [MIPS] time: Move...
16
  #include <asm/time.h>
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
17
18
19
20
21
22
23
24
  #include <asm/cevt-r4k.h>
  
  /*
   * The SMTC Kernel for the 34K, 1004K, et. al. replaces several
   * of these routines with SMTC-specific variants.
   */
  
  #ifndef CONFIG_MIPS_MT_SMTC
42f77542f   Ralf Baechle   [MIPS] time: Move...
25
26
27
28
29
30
  
  static int mips_next_event(unsigned long delta,
                             struct clock_event_device *evt)
  {
  	unsigned int cnt;
  	int res;
42f77542f   Ralf Baechle   [MIPS] time: Move...
31
32
33
  	cnt = read_c0_count();
  	cnt += delta;
  	write_c0_compare(cnt);
5878fc936   Kevin Cernekee   MIPS: Fix CP0 COU...
34
  	res = ((int)(read_c0_count() - cnt) >= 0) ? -ETIME : 0;
42f77542f   Ralf Baechle   [MIPS] time: Move...
35
36
  	return res;
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
37
38
39
40
  #endif /* CONFIG_MIPS_MT_SMTC */
  
  void mips_set_clock_mode(enum clock_event_mode mode,
  				struct clock_event_device *evt)
42f77542f   Ralf Baechle   [MIPS] time: Move...
41
42
43
  {
  	/* Nothing to do ...  */
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
44
45
  DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
  int cp0_timer_irq_installed;
42f77542f   Ralf Baechle   [MIPS] time: Move...
46

8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
47
  #ifndef CONFIG_MIPS_MT_SMTC
42f77542f   Ralf Baechle   [MIPS] time: Move...
48

8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
49
  irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
42f77542f   Ralf Baechle   [MIPS] time: Move...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  {
  	const int r2 = cpu_has_mips_r2;
  	struct clock_event_device *cd;
  	int cpu = smp_processor_id();
  
  	/*
  	 * Suckage alert:
  	 * Before R2 of the architecture there was no way to see if a
  	 * performance counter interrupt was pending, so we have to run
  	 * the performance counter interrupt handler anyway.
  	 */
  	if (handle_perf_irq(r2))
  		goto out;
  
  	/*
  	 * The same applies to performance counter interrupts.  But with the
  	 * above we now know that the reason we got here must be a timer
  	 * interrupt.  Being the paranoiacs we are we check anyway.
  	 */
  	if (!r2 || (read_c0_cause() & (1 << 30))) {
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
70
71
  		/* Clear Count/Compare Interrupt */
  		write_c0_compare(read_c0_compare());
42f77542f   Ralf Baechle   [MIPS] time: Move...
72
73
74
75
76
77
78
  		cd = &per_cpu(mips_clockevent_device, cpu);
  		cd->event_handler(cd);
  	}
  
  out:
  	return IRQ_HANDLED;
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
79
80
81
  #endif /* Not CONFIG_MIPS_MT_SMTC */
  
  struct irqaction c0_compare_irqaction = {
42f77542f   Ralf Baechle   [MIPS] time: Move...
82
  	.handler = c0_compare_interrupt,
8b5690f88   Yong Zhang   MIPS: irq: Remove...
83
  	.flags = IRQF_PERCPU | IRQF_TIMER,
42f77542f   Ralf Baechle   [MIPS] time: Move...
84
85
  	.name = "timer",
  };
42f77542f   Ralf Baechle   [MIPS] time: Move...
86

8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
87
  void mips_event_handler(struct clock_event_device *dev)
42f77542f   Ralf Baechle   [MIPS] time: Move...
88
89
90
91
92
93
94
95
  {
  }
  
  /*
   * FIXME: This doesn't hold for the relocated E9000 compare interrupt.
   */
  static int c0_compare_int_pending(void)
  {
010c108d7   David VomLehn   MIPS: PowerTV: Fi...
96
  	return (read_c0_cause() >> cp0_compare_irq_shift) & (1ul << CAUSEB_IP);
42f77542f   Ralf Baechle   [MIPS] time: Move...
97
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
98
99
  /*
   * Compare interrupt can be routed and latched outside the core,
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
100
101
   * so wait up to worst case number of cycle counter ticks for timer interrupt
   * changes to propagate to the cause register.
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
102
   */
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
103
  #define COMPARE_INT_SEEN_TICKS 50
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
104
105
  
  int c0_compare_int_usable(void)
42f77542f   Ralf Baechle   [MIPS] time: Move...
106
  {
3a6c43a78   Atsushi Nemoto   [MIPS] time: Make...
107
  	unsigned int delta;
42f77542f   Ralf Baechle   [MIPS] time: Move...
108
109
110
111
112
113
  	unsigned int cnt;
  
  	/*
  	 * IP7 already pending?  Try to clear it by acking the timer.
  	 */
  	if (c0_compare_int_pending()) {
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
114
115
116
117
118
119
  		cnt = read_c0_count();
  		write_c0_compare(cnt);
  		back_to_back_c0_hazard();
  		while (read_c0_count() < (cnt  + COMPARE_INT_SEEN_TICKS))
  			if (!c0_compare_int_pending())
  				break;
42f77542f   Ralf Baechle   [MIPS] time: Move...
120
121
122
  		if (c0_compare_int_pending())
  			return 0;
  	}
3a6c43a78   Atsushi Nemoto   [MIPS] time: Make...
123
124
125
126
  	for (delta = 0x10; delta <= 0x400000; delta <<= 1) {
  		cnt = read_c0_count();
  		cnt += delta;
  		write_c0_compare(cnt);
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
127
  		back_to_back_c0_hazard();
3a6c43a78   Atsushi Nemoto   [MIPS] time: Make...
128
129
130
131
  		if ((int)(read_c0_count() - cnt) < 0)
  		    break;
  		/* increase delta if the timer was already expired */
  	}
42f77542f   Ralf Baechle   [MIPS] time: Move...
132

c637fecb4   Atsushi Nemoto   [MIPS] time: Fix ...
133
  	while ((int)(read_c0_count() - cnt) <= 0)
42f77542f   Ralf Baechle   [MIPS] time: Move...
134
  		;	/* Wait for expiry  */
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
135
136
137
  	while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
  		if (c0_compare_int_pending())
  			break;
42f77542f   Ralf Baechle   [MIPS] time: Move...
138
139
  	if (!c0_compare_int_pending())
  		return 0;
4f1a1eb53   Al Cooper   MIPS: Kernel hang...
140
141
142
143
144
145
  	cnt = read_c0_count();
  	write_c0_compare(cnt);
  	back_to_back_c0_hazard();
  	while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
  		if (!c0_compare_int_pending())
  			break;
42f77542f   Ralf Baechle   [MIPS] time: Move...
146
147
148
149
150
151
152
153
  	if (c0_compare_int_pending())
  		return 0;
  
  	/*
  	 * Feels like a real count / compare timer.
  	 */
  	return 1;
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
154
  #ifndef CONFIG_MIPS_MT_SMTC
779e7d41a   Manuel Lauss   MIPS: make cp0 co...
155
  int __cpuinit r4k_clockevent_init(void)
42f77542f   Ralf Baechle   [MIPS] time: Move...
156
  {
42f77542f   Ralf Baechle   [MIPS] time: Move...
157
158
  	unsigned int cpu = smp_processor_id();
  	struct clock_event_device *cd;
38760d40c   Ralf Baechle   [MIPS] time: Repl...
159
  	unsigned int irq;
42f77542f   Ralf Baechle   [MIPS] time: Move...
160

22df3f53e   Yoichi Yuasa   [MIPS] Add mips_h...
161
  	if (!cpu_has_counter || !mips_hpt_frequency)
5aa85c9fc   Ralf Baechle   [MIPS] Handle R40...
162
  		return -ENXIO;
42f77542f   Ralf Baechle   [MIPS] time: Move...
163

42f77542f   Ralf Baechle   [MIPS] time: Move...
164
  	if (!c0_compare_int_usable())
5aa85c9fc   Ralf Baechle   [MIPS] Handle R40...
165
  		return -ENXIO;
42f77542f   Ralf Baechle   [MIPS] time: Move...
166

38760d40c   Ralf Baechle   [MIPS] time: Repl...
167
168
169
170
171
172
173
174
  	/*
  	 * With vectored interrupts things are getting platform specific.
  	 * get_c0_compare_int is a hook to allow a platform to return the
  	 * interrupt number of it's liking.
  	 */
  	irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
  	if (get_c0_compare_int)
  		irq = get_c0_compare_int();
42f77542f   Ralf Baechle   [MIPS] time: Move...
175
176
177
178
  	cd = &per_cpu(mips_clockevent_device, cpu);
  
  	cd->name		= "MIPS";
  	cd->features		= CLOCK_EVT_FEAT_ONESHOT;
4d2b11252   David Daney   MIPS: Don't overf...
179
  	clockevent_set_clock(cd, mips_hpt_frequency);
42f77542f   Ralf Baechle   [MIPS] time: Move...
180
  	/* Calculate the min / max delta */
42f77542f   Ralf Baechle   [MIPS] time: Move...
181
182
183
184
185
  	cd->max_delta_ns	= clockevent_delta2ns(0x7fffffff, cd);
  	cd->min_delta_ns	= clockevent_delta2ns(0x300, cd);
  
  	cd->rating		= 300;
  	cd->irq			= irq;
320ab2b0b   Rusty Russell   cpumask: convert ...
186
  	cd->cpumask		= cpumask_of(cpu);
42f77542f   Ralf Baechle   [MIPS] time: Move...
187
  	cd->set_next_event	= mips_next_event;
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
188
  	cd->set_mode		= mips_set_clock_mode;
42f77542f   Ralf Baechle   [MIPS] time: Move...
189
190
191
  	cd->event_handler	= mips_event_handler;
  
  	clockevents_register_device(cd);
aea686394   Ralf Baechle   [MIPS] time: Fix ...
192
  	if (cp0_timer_irq_installed)
5aa85c9fc   Ralf Baechle   [MIPS] Handle R40...
193
  		return 0;
38760d40c   Ralf Baechle   [MIPS] time: Repl...
194
195
  
  	cp0_timer_irq_installed = 1;
38760d40c   Ralf Baechle   [MIPS] time: Repl...
196
  	setup_irq(irq, &c0_compare_irqaction);
5aa85c9fc   Ralf Baechle   [MIPS] Handle R40...
197
198
  
  	return 0;
42f77542f   Ralf Baechle   [MIPS] time: Move...
199
  }
8531a35e5   Kevin D. Kissell   [MIPS] SMTC: Fix ...
200
201
  
  #endif /* Not CONFIG_MIPS_MT_SMTC */