Blame view

drivers/clocksource/timer-atmel-tcb.c 13.4 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
4d243f92e   David Brownell   atmel_tc clocksou...
2
3
4
5
6
7
8
  #include <linux/init.h>
  #include <linux/clocksource.h>
  #include <linux/clockchips.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  
  #include <linux/clk.h>
1ce861cec   Alexandre Belloni   clocksource/drive...
9
  #include <linux/delay.h>
4d243f92e   David Brownell   atmel_tc clocksou...
10
11
12
  #include <linux/err.h>
  #include <linux/ioport.h>
  #include <linux/io.h>
86232bfd2   Alexandre Belloni   clocksource/drive...
13
14
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
f712a1e8e   Alexandre Belloni   clocksource/drive...
15
  #include <linux/sched_clock.h>
2a515e5d7   Alexandre Belloni   clocksource/drive...
16
  #include <linux/syscore_ops.h>
c2c9136b7   Alexandre Belloni   ARM: at91: move S...
17
  #include <soc/at91/atmel_tcb.h>
4d243f92e   David Brownell   atmel_tc clocksou...
18
19
20
21
22
23
24
25
26
  
  
  /*
   * We're configured to use a specific TC block, one that's not hooked
   * up to external hardware, to provide a time solution:
   *
   *   - Two channels combine to create a free-running 32 bit counter
   *     with a base rate of 5+ MHz, packaged as a clocksource (with
   *     resolution better than 200 nsec).
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
27
28
   *   - Some chips support 32 bit counter. A single channel is used for
   *     this 32 bit free-running counter. the second channel is not used.
4d243f92e   David Brownell   atmel_tc clocksou...
29
   *
ef1d6a20e   Alexandre Belloni   clocksource/drive...
30
31
32
33
   *   - The third channel may be used to provide a clockevent source, used in
   *   either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ,
   *   and can handle delays of up to two seconds. For 32-bit counters, it runs at
   *   the same rate as the clocksource
4d243f92e   David Brownell   atmel_tc clocksou...
34
   *
4d243f92e   David Brownell   atmel_tc clocksou...
35
36
37
38
39
40
41
   * REVISIT behavior during system suspend states... we should disable
   * all clocks and save the power.  Easily done for clockevent devices,
   * but clocksources won't necessarily get the needed notifications.
   * For deeper system sleep states, this will be mandatory...
   */
  
  static void __iomem *tcaddr;
2a515e5d7   Alexandre Belloni   clocksource/drive...
42
43
44
45
46
47
48
49
  static struct
  {
  	u32 cmr;
  	u32 imr;
  	u32 rc;
  	bool clken;
  } tcb_cache[3];
  static u32 bmr_cache;
4d243f92e   David Brownell   atmel_tc clocksou...
50

ef1d6a20e   Alexandre Belloni   clocksource/drive...
51
  static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 };
a5a1d1c29   Thomas Gleixner   clocksource: Use ...
52
  static u64 tc_get_cycles(struct clocksource *cs)
4d243f92e   David Brownell   atmel_tc clocksou...
53
54
55
56
57
58
  {
  	unsigned long	flags;
  	u32		lower, upper;
  
  	raw_local_irq_save(flags);
  	do {
6ec8be251   Alexandre Belloni   clocksource/drive...
59
60
61
  		upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV));
  		lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
  	} while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)));
4d243f92e   David Brownell   atmel_tc clocksou...
62
63
64
65
  
  	raw_local_irq_restore(flags);
  	return (upper << 16) | lower;
  }
7b9f1d16e   David Engraf   clocksource/drive...
66
67
  static u64 tc_get_cycles32(struct clocksource *cs)
  {
6ec8be251   Alexandre Belloni   clocksource/drive...
68
  	return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
7b9f1d16e   David Engraf   clocksource/drive...
69
  }
7ebe68100   kbuild test robot   clocksource/drive...
70
  static void tc_clksrc_suspend(struct clocksource *cs)
2a515e5d7   Alexandre Belloni   clocksource/drive...
71
72
73
74
75
76
77
78
79
80
81
82
83
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
  		tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR));
  		tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR));
  		tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC));
  		tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) &
  					ATMEL_TC_CLKSTA);
  	}
  
  	bmr_cache = readl(tcaddr + ATMEL_TC_BMR);
  }
7ebe68100   kbuild test robot   clocksource/drive...
84
  static void tc_clksrc_resume(struct clocksource *cs)
2a515e5d7   Alexandre Belloni   clocksource/drive...
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
  		/* Restore registers for the channel, RA and RB are not used  */
  		writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR));
  		writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC));
  		writel(0, tcaddr + ATMEL_TC_REG(i, RA));
  		writel(0, tcaddr + ATMEL_TC_REG(i, RB));
  		/* Disable all the interrupts */
  		writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR));
  		/* Reenable interrupts that were enabled before suspending */
  		writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER));
  		/* Start the clock if it was used */
  		if (tcb_cache[i].clken)
  			writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR));
  	}
  
  	/* Dual channel, chain channels */
  	writel(bmr_cache, tcaddr + ATMEL_TC_BMR);
  	/* Finally, trigger all the channels*/
  	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
  }
4d243f92e   David Brownell   atmel_tc clocksou...
108
  static struct clocksource clksrc = {
4d243f92e   David Brownell   atmel_tc clocksou...
109
110
111
  	.rating         = 200,
  	.read           = tc_get_cycles,
  	.mask           = CLOCKSOURCE_MASK(32),
4d243f92e   David Brownell   atmel_tc clocksou...
112
  	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
2a515e5d7   Alexandre Belloni   clocksource/drive...
113
114
  	.suspend	= tc_clksrc_suspend,
  	.resume		= tc_clksrc_resume,
4d243f92e   David Brownell   atmel_tc clocksou...
115
  };
f712a1e8e   Alexandre Belloni   clocksource/drive...
116
117
118
119
120
121
122
123
124
  static u64 notrace tc_sched_clock_read(void)
  {
  	return tc_get_cycles(&clksrc);
  }
  
  static u64 notrace tc_sched_clock_read32(void)
  {
  	return tc_get_cycles32(&clksrc);
  }
1ce861cec   Alexandre Belloni   clocksource/drive...
125
126
127
128
129
130
131
132
133
134
135
  static struct delay_timer tc_delay_timer;
  
  static unsigned long tc_delay_timer_read(void)
  {
  	return tc_get_cycles(&clksrc);
  }
  
  static unsigned long notrace tc_delay_timer_read32(void)
  {
  	return tc_get_cycles32(&clksrc);
  }
4d243f92e   David Brownell   atmel_tc clocksou...
136
137
138
139
140
  #ifdef CONFIG_GENERIC_CLOCKEVENTS
  
  struct tc_clkevt_device {
  	struct clock_event_device	clkevt;
  	struct clk			*clk;
ef1d6a20e   Alexandre Belloni   clocksource/drive...
141
  	u32				rate;
4d243f92e   David Brownell   atmel_tc clocksou...
142
143
144
145
146
147
148
  	void __iomem			*regs;
  };
  
  static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
  {
  	return container_of(clkevt, struct tc_clkevt_device, clkevt);
  }
4d243f92e   David Brownell   atmel_tc clocksou...
149
  static u32 timer_clock;
cf4541c10   Viresh Kumar   clockevents/drive...
150
  static int tc_shutdown(struct clock_event_device *d)
4d243f92e   David Brownell   atmel_tc clocksou...
151
152
153
  {
  	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
  	void __iomem		*regs = tcd->regs;
6ec8be251   Alexandre Belloni   clocksource/drive...
154
155
  	writel(0xff, regs + ATMEL_TC_REG(2, IDR));
  	writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
f02b4b72d   Alexandre Belloni   clockevents/tcb_c...
156
157
  	if (!clockevent_state_detached(d))
  		clk_disable(tcd->clk);
4d243f92e   David Brownell   atmel_tc clocksou...
158

cf4541c10   Viresh Kumar   clockevents/drive...
159
160
  	return 0;
  }
4d243f92e   David Brownell   atmel_tc clocksou...
161

cf4541c10   Viresh Kumar   clockevents/drive...
162
163
164
165
  static int tc_set_oneshot(struct clock_event_device *d)
  {
  	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
  	void __iomem		*regs = tcd->regs;
4d243f92e   David Brownell   atmel_tc clocksou...
166

cf4541c10   Viresh Kumar   clockevents/drive...
167
168
  	if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
  		tc_shutdown(d);
4d243f92e   David Brownell   atmel_tc clocksou...
169

cf4541c10   Viresh Kumar   clockevents/drive...
170
  	clk_enable(tcd->clk);
4d243f92e   David Brownell   atmel_tc clocksou...
171

ef1d6a20e   Alexandre Belloni   clocksource/drive...
172
  	/* count up to RC, then irq and stop */
6ec8be251   Alexandre Belloni   clocksource/drive...
173
  	writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
cf4541c10   Viresh Kumar   clockevents/drive...
174
  		     ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
6ec8be251   Alexandre Belloni   clocksource/drive...
175
  	writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
4d243f92e   David Brownell   atmel_tc clocksou...
176

cf4541c10   Viresh Kumar   clockevents/drive...
177
178
179
  	/* set_next_event() configures and starts the timer */
  	return 0;
  }
4d243f92e   David Brownell   atmel_tc clocksou...
180

cf4541c10   Viresh Kumar   clockevents/drive...
181
182
183
184
  static int tc_set_periodic(struct clock_event_device *d)
  {
  	struct tc_clkevt_device *tcd = to_tc_clkevt(d);
  	void __iomem		*regs = tcd->regs;
4d243f92e   David Brownell   atmel_tc clocksou...
185

cf4541c10   Viresh Kumar   clockevents/drive...
186
187
  	if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
  		tc_shutdown(d);
4d243f92e   David Brownell   atmel_tc clocksou...
188

cf4541c10   Viresh Kumar   clockevents/drive...
189
190
191
192
  	/* By not making the gentime core emulate periodic mode on top
  	 * of oneshot, we get lower overhead and improved accuracy.
  	 */
  	clk_enable(tcd->clk);
ef1d6a20e   Alexandre Belloni   clocksource/drive...
193
  	/* count up to RC, then irq and restart */
6ec8be251   Alexandre Belloni   clocksource/drive...
194
  	writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
cf4541c10   Viresh Kumar   clockevents/drive...
195
  		     regs + ATMEL_TC_REG(2, CMR));
ef1d6a20e   Alexandre Belloni   clocksource/drive...
196
  	writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
cf4541c10   Viresh Kumar   clockevents/drive...
197
198
  
  	/* Enable clock and interrupts on RC compare */
6ec8be251   Alexandre Belloni   clocksource/drive...
199
  	writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
cf4541c10   Viresh Kumar   clockevents/drive...
200
201
  
  	/* go go gadget! */
6ec8be251   Alexandre Belloni   clocksource/drive...
202
  	writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs +
cf4541c10   Viresh Kumar   clockevents/drive...
203
204
  		     ATMEL_TC_REG(2, CCR));
  	return 0;
4d243f92e   David Brownell   atmel_tc clocksou...
205
206
207
208
  }
  
  static int tc_next_event(unsigned long delta, struct clock_event_device *d)
  {
6ec8be251   Alexandre Belloni   clocksource/drive...
209
  	writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC));
4d243f92e   David Brownell   atmel_tc clocksou...
210
211
  
  	/* go go gadget! */
6ec8be251   Alexandre Belloni   clocksource/drive...
212
  	writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
4d243f92e   David Brownell   atmel_tc clocksou...
213
214
215
216
217
218
  			tcaddr + ATMEL_TC_REG(2, CCR));
  	return 0;
  }
  
  static struct tc_clkevt_device clkevt = {
  	.clkevt	= {
cf4541c10   Viresh Kumar   clockevents/drive...
219
220
  		.features		= CLOCK_EVT_FEAT_PERIODIC |
  					  CLOCK_EVT_FEAT_ONESHOT,
4d243f92e   David Brownell   atmel_tc clocksou...
221
  		/* Should be lower than at91rm9200's system timer */
cf4541c10   Viresh Kumar   clockevents/drive...
222
223
224
225
226
  		.rating			= 125,
  		.set_next_event		= tc_next_event,
  		.set_state_shutdown	= tc_shutdown,
  		.set_state_periodic	= tc_set_periodic,
  		.set_state_oneshot	= tc_set_oneshot,
4d243f92e   David Brownell   atmel_tc clocksou...
227
228
229
230
231
232
233
  	},
  };
  
  static irqreturn_t ch2_irq(int irq, void *handle)
  {
  	struct tc_clkevt_device	*dev = handle;
  	unsigned int		sr;
6ec8be251   Alexandre Belloni   clocksource/drive...
234
  	sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR));
4d243f92e   David Brownell   atmel_tc clocksou...
235
236
237
238
239
240
241
  	if (sr & ATMEL_TC_CPCS) {
  		dev->clkevt.event_handler(&dev->clkevt);
  		return IRQ_HANDLED;
  	}
  
  	return IRQ_NONE;
  }
ef1d6a20e   Alexandre Belloni   clocksource/drive...
242
  static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
4d243f92e   David Brownell   atmel_tc clocksou...
243
  {
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
244
  	int ret;
4d243f92e   David Brownell   atmel_tc clocksou...
245
246
  	struct clk *t2_clk = tc->clk[2];
  	int irq = tc->irq[2];
ef1d6a20e   Alexandre Belloni   clocksource/drive...
247
  	int bits = tc->tcb_config->counter_width;
7d8d05d11   Boris Brezillon   misc: atmel_tclib...
248

5b3c11da1   Boris BREZILLON   clocksource: tcb_...
249
250
  	/* try to enable t2 clk to avoid future errors in mode change */
  	ret = clk_prepare_enable(t2_clk);
ef1d6a20e   Alexandre Belloni   clocksource/drive...
251
  	if (ret)
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
252
  		return ret;
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
253

4d243f92e   David Brownell   atmel_tc clocksou...
254
255
  	clkevt.regs = tc->regs;
  	clkevt.clk = t2_clk;
4d243f92e   David Brownell   atmel_tc clocksou...
256

ef1d6a20e   Alexandre Belloni   clocksource/drive...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  	if (bits == 32) {
  		timer_clock = divisor_idx;
  		clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx];
  	} else {
  		ret = clk_prepare_enable(tc->slow_clk);
  		if (ret) {
  			clk_disable_unprepare(t2_clk);
  			return ret;
  		}
  
  		clkevt.rate = clk_get_rate(tc->slow_clk);
  		timer_clock = ATMEL_TC_TIMER_CLOCK5;
  	}
  
  	clk_disable(t2_clk);
4d243f92e   David Brownell   atmel_tc clocksou...
272

320ab2b0b   Rusty Russell   cpumask: convert ...
273
  	clkevt.clkevt.cpumask = cpumask_of(0);
4d243f92e   David Brownell   atmel_tc clocksou...
274

d07a1ecdf   Gaël PORTAY   clocksource: tcb_...
275
276
  	ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
  	if (ret) {
eed9fb9df   Boris Brezillon   clocksource: tcb_...
277
  		clk_unprepare(t2_clk);
ef1d6a20e   Alexandre Belloni   clocksource/drive...
278
279
  		if (bits != 32)
  			clk_disable_unprepare(tc->slow_clk);
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
280
  		return ret;
d07a1ecdf   Gaël PORTAY   clocksource: tcb_...
281
  	}
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
282

ef1d6a20e   Alexandre Belloni   clocksource/drive...
283
  	clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1);
1817dc037   Voss, Nikolaus   drivers/clocksour...
284

5b3c11da1   Boris BREZILLON   clocksource: tcb_...
285
  	return ret;
4d243f92e   David Brownell   atmel_tc clocksou...
286
287
288
  }
  
  #else /* !CONFIG_GENERIC_CLOCKEVENTS */
ef1d6a20e   Alexandre Belloni   clocksource/drive...
289
  static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
4d243f92e   David Brownell   atmel_tc clocksou...
290
291
  {
  	/* NOTHING */
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
292
  	return 0;
4d243f92e   David Brownell   atmel_tc clocksou...
293
294
295
  }
  
  #endif
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
296
297
298
  static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
  {
  	/* channel 0:  waveform mode, input mclk/8, clock TIOA0 on overflow */
6ec8be251   Alexandre Belloni   clocksource/drive...
299
  	writel(mck_divisor_idx			/* likely divide-by-8 */
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
300
301
302
303
304
  			| ATMEL_TC_WAVE
  			| ATMEL_TC_WAVESEL_UP		/* free-run */
  			| ATMEL_TC_ACPA_SET		/* TIOA0 rises at 0 */
  			| ATMEL_TC_ACPC_CLEAR,		/* (duty cycle 50%) */
  			tcaddr + ATMEL_TC_REG(0, CMR));
6ec8be251   Alexandre Belloni   clocksource/drive...
305
306
307
308
  	writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
  	writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
  	writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR));	/* no irqs */
  	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
309
310
  
  	/* channel 1:  waveform mode, input TIOA0 */
6ec8be251   Alexandre Belloni   clocksource/drive...
311
  	writel(ATMEL_TC_XC1			/* input: TIOA0 */
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
312
313
314
  			| ATMEL_TC_WAVE
  			| ATMEL_TC_WAVESEL_UP,		/* free-run */
  			tcaddr + ATMEL_TC_REG(1, CMR));
6ec8be251   Alexandre Belloni   clocksource/drive...
315
316
  	writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR));	/* no irqs */
  	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
317
318
  
  	/* chain channel 0 to channel 1*/
6ec8be251   Alexandre Belloni   clocksource/drive...
319
  	writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
320
  	/* then reset all the timers */
6ec8be251   Alexandre Belloni   clocksource/drive...
321
  	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
322
323
324
325
326
  }
  
  static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
  {
  	/* channel 0:  waveform mode, input mclk/8 */
6ec8be251   Alexandre Belloni   clocksource/drive...
327
  	writel(mck_divisor_idx			/* likely divide-by-8 */
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
328
329
330
  			| ATMEL_TC_WAVE
  			| ATMEL_TC_WAVESEL_UP,		/* free-run */
  			tcaddr + ATMEL_TC_REG(0, CMR));
6ec8be251   Alexandre Belloni   clocksource/drive...
331
332
  	writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR));	/* no irqs */
  	writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
333
334
  
  	/* then reset all the timers */
6ec8be251   Alexandre Belloni   clocksource/drive...
335
  	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
336
  }
d2c60dcf8   Alexandre Belloni   clocksource/drive...
337
338
339
340
341
342
343
  static struct atmel_tcb_config tcb_rm9200_config = {
  	.counter_width = 16,
  };
  
  static struct atmel_tcb_config tcb_sam9x5_config = {
  	.counter_width = 32,
  };
467ae18aa   Alexandre Belloni   clocksource/drive...
344
345
346
347
  static struct atmel_tcb_config tcb_sama5d2_config = {
  	.counter_width = 32,
  	.has_gclk = 1,
  };
86232bfd2   Alexandre Belloni   clocksource/drive...
348
  static const struct of_device_id atmel_tcb_of_match[] = {
d2c60dcf8   Alexandre Belloni   clocksource/drive...
349
350
  	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
  	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
467ae18aa   Alexandre Belloni   clocksource/drive...
351
  	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
86232bfd2   Alexandre Belloni   clocksource/drive...
352
353
  	{ /* sentinel */ }
  };
4d243f92e   David Brownell   atmel_tc clocksou...
354

86232bfd2   Alexandre Belloni   clocksource/drive...
355
356
357
  static int __init tcb_clksrc_init(struct device_node *node)
  {
  	struct atmel_tc tc;
3ee08aea7   David Brownell   tclib: Fix compil...
358
  	struct clk *t0_clk;
86232bfd2   Alexandre Belloni   clocksource/drive...
359
  	const struct of_device_id *match;
f712a1e8e   Alexandre Belloni   clocksource/drive...
360
  	u64 (*tc_sched_clock)(void);
4d243f92e   David Brownell   atmel_tc clocksou...
361
362
  	u32 rate, divided_rate = 0;
  	int best_divisor_idx = -1;
86232bfd2   Alexandre Belloni   clocksource/drive...
363
  	int bits;
4d243f92e   David Brownell   atmel_tc clocksou...
364
  	int i;
0e746ec55   Boris BREZILLON   clocksource: tcb_...
365
  	int ret;
4d243f92e   David Brownell   atmel_tc clocksou...
366

86232bfd2   Alexandre Belloni   clocksource/drive...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  	/* Protect against multiple calls */
  	if (tcaddr)
  		return 0;
  
  	tc.regs = of_iomap(node->parent, 0);
  	if (!tc.regs)
  		return -ENXIO;
  
  	t0_clk = of_clk_get_by_name(node->parent, "t0_clk");
  	if (IS_ERR(t0_clk))
  		return PTR_ERR(t0_clk);
  
  	tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk");
  	if (IS_ERR(tc.slow_clk))
  		return PTR_ERR(tc.slow_clk);
  
  	tc.clk[0] = t0_clk;
  	tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk");
  	if (IS_ERR(tc.clk[1]))
  		tc.clk[1] = t0_clk;
  	tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk");
  	if (IS_ERR(tc.clk[2]))
  		tc.clk[2] = t0_clk;
  
  	tc.irq[2] = of_irq_get(node->parent, 2);
  	if (tc.irq[2] <= 0) {
  		tc.irq[2] = of_irq_get(node->parent, 0);
  		if (tc.irq[2] <= 0)
  			return -EINVAL;
4d243f92e   David Brownell   atmel_tc clocksou...
396
  	}
4d243f92e   David Brownell   atmel_tc clocksou...
397

86232bfd2   Alexandre Belloni   clocksource/drive...
398
  	match = of_match_node(atmel_tcb_of_match, node->parent);
d2c60dcf8   Alexandre Belloni   clocksource/drive...
399
400
401
402
403
  	if (!match)
  		return -ENODEV;
  
  	tc.tcb_config = match->data;
  	bits = tc.tcb_config->counter_width;
86232bfd2   Alexandre Belloni   clocksource/drive...
404
405
406
  
  	for (i = 0; i < ARRAY_SIZE(tc.irq); i++)
  		writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR));
0e746ec55   Boris BREZILLON   clocksource: tcb_...
407
408
409
410
  	ret = clk_prepare_enable(t0_clk);
  	if (ret) {
  		pr_debug("can't enable T0 clk
  ");
86232bfd2   Alexandre Belloni   clocksource/drive...
411
  		return ret;
0e746ec55   Boris BREZILLON   clocksource: tcb_...
412
  	}
4d243f92e   David Brownell   atmel_tc clocksou...
413
414
415
  
  	/* How fast will we be counting?  Pick something over 5 MHz.  */
  	rate = (u32) clk_get_rate(t0_clk);
467ae18aa   Alexandre Belloni   clocksource/drive...
416
417
418
419
  	i = 0;
  	if (tc.tcb_config->has_gclk)
  		i = 1;
  	for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) {
86232bfd2   Alexandre Belloni   clocksource/drive...
420
  		unsigned divisor = atmel_tcb_divisors[i];
4d243f92e   David Brownell   atmel_tc clocksou...
421
  		unsigned tmp;
4d243f92e   David Brownell   atmel_tc clocksou...
422
423
424
  		tmp = rate / divisor;
  		pr_debug("TC: %u / %-3u [%d] --> %u
  ", rate, divisor, i, tmp);
501465d5d   Alexandre Belloni   clocksource/drive...
425
426
  		if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000))
  			break;
4d243f92e   David Brownell   atmel_tc clocksou...
427
428
429
  		divided_rate = tmp;
  		best_divisor_idx = i;
  	}
86232bfd2   Alexandre Belloni   clocksource/drive...
430
431
432
433
  	clksrc.name = kbasename(node->parent->full_name);
  	clkevt.clkevt.name = kbasename(node->parent->full_name);
  	pr_debug("%s at %d.%03d MHz
  ", clksrc.name, divided_rate / 1000000,
542f82460   Romain Izard   clocksource/drive...
434
  			((divided_rate % 1000000) + 500) / 1000);
4d243f92e   David Brownell   atmel_tc clocksou...
435

86232bfd2   Alexandre Belloni   clocksource/drive...
436
437
438
  	tcaddr = tc.regs;
  
  	if (bits == 32) {
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
439
440
441
  		/* use apropriate function to read 32 bit counter */
  		clksrc.read = tc_get_cycles32;
  		/* setup ony channel 0 */
86232bfd2   Alexandre Belloni   clocksource/drive...
442
  		tcb_setup_single_chan(&tc, best_divisor_idx);
f712a1e8e   Alexandre Belloni   clocksource/drive...
443
  		tc_sched_clock = tc_sched_clock_read32;
1ce861cec   Alexandre Belloni   clocksource/drive...
444
  		tc_delay_timer.read_current_timer = tc_delay_timer_read32;
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
445
  	} else {
86232bfd2   Alexandre Belloni   clocksource/drive...
446
  		/* we have three clocks no matter what the
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
447
448
  		 * underlying platform supports.
  		 */
86232bfd2   Alexandre Belloni   clocksource/drive...
449
  		ret = clk_prepare_enable(tc.clk[1]);
0e746ec55   Boris BREZILLON   clocksource: tcb_...
450
451
452
453
454
  		if (ret) {
  			pr_debug("can't enable T1 clk
  ");
  			goto err_disable_t0;
  		}
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
455
  		/* setup both channel 0 & 1 */
86232bfd2   Alexandre Belloni   clocksource/drive...
456
  		tcb_setup_dual_chan(&tc, best_divisor_idx);
f712a1e8e   Alexandre Belloni   clocksource/drive...
457
  		tc_sched_clock = tc_sched_clock_read;
1ce861cec   Alexandre Belloni   clocksource/drive...
458
  		tc_delay_timer.read_current_timer = tc_delay_timer_read;
8e315a7b0   Nicolas Ferre   ARM: at91/tc/cloc...
459
  	}
4d243f92e   David Brownell   atmel_tc clocksou...
460
461
  
  	/* and away we go! */
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
462
463
464
  	ret = clocksource_register_hz(&clksrc, divided_rate);
  	if (ret)
  		goto err_disable_t1;
4d243f92e   David Brownell   atmel_tc clocksou...
465
466
  
  	/* channel 2:  periodic and oneshot timer support */
ef1d6a20e   Alexandre Belloni   clocksource/drive...
467
  	ret = setup_clkevents(&tc, best_divisor_idx);
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
468
469
  	if (ret)
  		goto err_unregister_clksrc;
4d243f92e   David Brownell   atmel_tc clocksou...
470

f712a1e8e   Alexandre Belloni   clocksource/drive...
471
  	sched_clock_register(tc_sched_clock, 32, divided_rate);
1ce861cec   Alexandre Belloni   clocksource/drive...
472
473
  	tc_delay_timer.freq = divided_rate;
  	register_current_timer_delay(&tc_delay_timer);
4d243f92e   David Brownell   atmel_tc clocksou...
474
  	return 0;
0e746ec55   Boris BREZILLON   clocksource: tcb_...
475

5b3c11da1   Boris BREZILLON   clocksource: tcb_...
476
477
478
479
  err_unregister_clksrc:
  	clocksource_unregister(&clksrc);
  
  err_disable_t1:
86232bfd2   Alexandre Belloni   clocksource/drive...
480
481
  	if (bits != 32)
  		clk_disable_unprepare(tc.clk[1]);
5b3c11da1   Boris BREZILLON   clocksource: tcb_...
482

0e746ec55   Boris BREZILLON   clocksource: tcb_...
483
484
  err_disable_t0:
  	clk_disable_unprepare(t0_clk);
86232bfd2   Alexandre Belloni   clocksource/drive...
485
  	tcaddr = NULL;
0e746ec55   Boris BREZILLON   clocksource: tcb_...
486
  	return ret;
4d243f92e   David Brownell   atmel_tc clocksou...
487
  }
86232bfd2   Alexandre Belloni   clocksource/drive...
488
  TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init);