Blame view

drivers/clocksource/sh_tmu.c 15.2 KB
9570ef204   Magnus Damm   clocksource: Supe...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * SuperH Timer Support - TMU
   *
   *  Copyright (C) 2009 Magnus Damm
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
9570ef204   Magnus Damm   clocksource: Supe...
14
   */
13931f806   Laurent Pinchart   clocksource: sh_t...
15
16
17
18
19
  #include <linux/clk.h>
  #include <linux/clockchips.h>
  #include <linux/clocksource.h>
  #include <linux/delay.h>
  #include <linux/err.h>
9570ef204   Magnus Damm   clocksource: Supe...
20
  #include <linux/init.h>
9570ef204   Magnus Damm   clocksource: Supe...
21
  #include <linux/interrupt.h>
9570ef204   Magnus Damm   clocksource: Supe...
22
  #include <linux/io.h>
13931f806   Laurent Pinchart   clocksource: sh_t...
23
  #include <linux/ioport.h>
9570ef204   Magnus Damm   clocksource: Supe...
24
  #include <linux/irq.h>
7deeab5dc   Paul Gortmaker   drivers/clocksour...
25
  #include <linux/module.h>
3e29b5543   Laurent Pinchart   clocksource: sh_t...
26
  #include <linux/of.h>
13931f806   Laurent Pinchart   clocksource: sh_t...
27
  #include <linux/platform_device.h>
2ee619f94   Rafael J. Wysocki   PM / shmobile: Ma...
28
  #include <linux/pm_domain.h>
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
29
  #include <linux/pm_runtime.h>
13931f806   Laurent Pinchart   clocksource: sh_t...
30
31
32
  #include <linux/sh_timer.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
9570ef204   Magnus Damm   clocksource: Supe...
33

8c7f21e67   Laurent Pinchart   clocksource: sh_t...
34
  enum sh_tmu_model {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
35
36
37
  	SH_TMU,
  	SH_TMU_SH3,
  };
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
38
  struct sh_tmu_device;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
39
40
  
  struct sh_tmu_channel {
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
41
  	struct sh_tmu_device *tmu;
fe68eb802   Laurent Pinchart   clocksource: sh_t...
42
  	unsigned int index;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
43

de693461b   Laurent Pinchart   clocksource: sh_t...
44
  	void __iomem *base;
1c56cf6b0   Laurent Pinchart   clocksource: sh_t...
45
  	int irq;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
46

9570ef204   Magnus Damm   clocksource: Supe...
47
48
49
  	unsigned long periodic;
  	struct clock_event_device ced;
  	struct clocksource cs;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
50
  	bool cs_enabled;
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
51
  	unsigned int enable_count;
9570ef204   Magnus Damm   clocksource: Supe...
52
  };
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
53
  struct sh_tmu_device {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
54
55
56
57
  	struct platform_device *pdev;
  
  	void __iomem *mapbase;
  	struct clk *clk;
c3c0a20df   Nicolai Stange   clocksource: sh_t...
58
  	unsigned long rate;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
59

8c7f21e67   Laurent Pinchart   clocksource: sh_t...
60
  	enum sh_tmu_model model;
2b027f1f0   Laurent Pinchart   clocksource: sh_t...
61
  	raw_spinlock_t lock; /* Protect the shared start/stop register */
a5de49f43   Laurent Pinchart   clocksource: sh_t...
62
63
  	struct sh_tmu_channel *channels;
  	unsigned int num_channels;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
64
65
66
  
  	bool has_clockevent;
  	bool has_clocksource;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
67
  };
9570ef204   Magnus Damm   clocksource: Supe...
68
69
70
71
  #define TSTR -1 /* shared register */
  #define TCOR  0 /* channel register */
  #define TCNT 1 /* channel register */
  #define TCR 2 /* channel register */
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
72
73
74
75
76
77
78
79
  #define TCR_UNF			(1 << 8)
  #define TCR_UNIE		(1 << 5)
  #define TCR_TPSC_CLK4		(0 << 0)
  #define TCR_TPSC_CLK16		(1 << 0)
  #define TCR_TPSC_CLK64		(2 << 0)
  #define TCR_TPSC_CLK256		(3 << 0)
  #define TCR_TPSC_CLK1024	(4 << 0)
  #define TCR_TPSC_MASK		(7 << 0)
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
80
  static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
9570ef204   Magnus Damm   clocksource: Supe...
81
  {
9570ef204   Magnus Damm   clocksource: Supe...
82
  	unsigned long offs;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
83
84
  	if (reg_nr == TSTR) {
  		switch (ch->tmu->model) {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
85
86
87
88
89
90
  		case SH_TMU_SH3:
  			return ioread8(ch->tmu->mapbase + 2);
  		case SH_TMU:
  			return ioread8(ch->tmu->mapbase + 4);
  		}
  	}
9570ef204   Magnus Damm   clocksource: Supe...
91
92
93
94
  
  	offs = reg_nr << 2;
  
  	if (reg_nr == TCR)
de693461b   Laurent Pinchart   clocksource: sh_t...
95
  		return ioread16(ch->base + offs);
9570ef204   Magnus Damm   clocksource: Supe...
96
  	else
de693461b   Laurent Pinchart   clocksource: sh_t...
97
  		return ioread32(ch->base + offs);
9570ef204   Magnus Damm   clocksource: Supe...
98
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
99
  static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
9570ef204   Magnus Damm   clocksource: Supe...
100
101
  				unsigned long value)
  {
9570ef204   Magnus Damm   clocksource: Supe...
102
103
104
  	unsigned long offs;
  
  	if (reg_nr == TSTR) {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
105
  		switch (ch->tmu->model) {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
106
107
108
109
110
  		case SH_TMU_SH3:
  			return iowrite8(value, ch->tmu->mapbase + 2);
  		case SH_TMU:
  			return iowrite8(value, ch->tmu->mapbase + 4);
  		}
9570ef204   Magnus Damm   clocksource: Supe...
111
112
113
114
115
  	}
  
  	offs = reg_nr << 2;
  
  	if (reg_nr == TCR)
de693461b   Laurent Pinchart   clocksource: sh_t...
116
  		iowrite16(value, ch->base + offs);
9570ef204   Magnus Damm   clocksource: Supe...
117
  	else
de693461b   Laurent Pinchart   clocksource: sh_t...
118
  		iowrite32(value, ch->base + offs);
9570ef204   Magnus Damm   clocksource: Supe...
119
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
120
  static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
9570ef204   Magnus Damm   clocksource: Supe...
121
  {
9570ef204   Magnus Damm   clocksource: Supe...
122
123
124
  	unsigned long flags, value;
  
  	/* start stop register shared by multiple timer channels */
2b027f1f0   Laurent Pinchart   clocksource: sh_t...
125
  	raw_spin_lock_irqsave(&ch->tmu->lock, flags);
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
126
  	value = sh_tmu_read(ch, TSTR);
9570ef204   Magnus Damm   clocksource: Supe...
127
128
  
  	if (start)
fe68eb802   Laurent Pinchart   clocksource: sh_t...
129
  		value |= 1 << ch->index;
9570ef204   Magnus Damm   clocksource: Supe...
130
  	else
fe68eb802   Laurent Pinchart   clocksource: sh_t...
131
  		value &= ~(1 << ch->index);
9570ef204   Magnus Damm   clocksource: Supe...
132

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
133
  	sh_tmu_write(ch, TSTR, value);
2b027f1f0   Laurent Pinchart   clocksource: sh_t...
134
  	raw_spin_unlock_irqrestore(&ch->tmu->lock, flags);
9570ef204   Magnus Damm   clocksource: Supe...
135
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
136
  static int __sh_tmu_enable(struct sh_tmu_channel *ch)
9570ef204   Magnus Damm   clocksource: Supe...
137
  {
9570ef204   Magnus Damm   clocksource: Supe...
138
  	int ret;
d4905ce38   Paul Mundt   Revert "clocksour...
139
  	/* enable clock */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
140
  	ret = clk_enable(ch->tmu->clk);
9570ef204   Magnus Damm   clocksource: Supe...
141
  	if (ret) {
fe68eb802   Laurent Pinchart   clocksource: sh_t...
142
143
144
  		dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock
  ",
  			ch->index);
9570ef204   Magnus Damm   clocksource: Supe...
145
146
147
148
  		return ret;
  	}
  
  	/* make sure channel is disabled */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
149
  	sh_tmu_start_stop_ch(ch, 0);
9570ef204   Magnus Damm   clocksource: Supe...
150
151
  
  	/* maximum timeout */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
152
153
  	sh_tmu_write(ch, TCOR, 0xffffffff);
  	sh_tmu_write(ch, TCNT, 0xffffffff);
9570ef204   Magnus Damm   clocksource: Supe...
154
155
  
  	/* configure channel to parent clock / 4, irq off */
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
156
  	sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
9570ef204   Magnus Damm   clocksource: Supe...
157
158
  
  	/* enable channel */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
159
  	sh_tmu_start_stop_ch(ch, 1);
9570ef204   Magnus Damm   clocksource: Supe...
160
161
162
  
  	return 0;
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
163
  static int sh_tmu_enable(struct sh_tmu_channel *ch)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
164
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
165
  	if (ch->enable_count++ > 0)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
166
  		return 0;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
167
168
  	pm_runtime_get_sync(&ch->tmu->pdev->dev);
  	dev_pm_syscore_device(&ch->tmu->pdev->dev, true);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
169

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
170
  	return __sh_tmu_enable(ch);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
171
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
172
  static void __sh_tmu_disable(struct sh_tmu_channel *ch)
9570ef204   Magnus Damm   clocksource: Supe...
173
174
  {
  	/* disable channel */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
175
  	sh_tmu_start_stop_ch(ch, 0);
9570ef204   Magnus Damm   clocksource: Supe...
176

be890a1a9   Magnus Damm   sh: turn off irqs...
177
  	/* disable interrupts in TMU block */
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
178
  	sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
be890a1a9   Magnus Damm   sh: turn off irqs...
179

d4905ce38   Paul Mundt   Revert "clocksour...
180
  	/* stop clock */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
181
  	clk_disable(ch->tmu->clk);
9570ef204   Magnus Damm   clocksource: Supe...
182
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
183
  static void sh_tmu_disable(struct sh_tmu_channel *ch)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
184
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
185
  	if (WARN_ON(ch->enable_count == 0))
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
186
  		return;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
187
  	if (--ch->enable_count > 0)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
188
  		return;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
189
  	__sh_tmu_disable(ch);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
190

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
191
192
  	dev_pm_syscore_device(&ch->tmu->pdev->dev, false);
  	pm_runtime_put(&ch->tmu->pdev->dev);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
193
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
194
  static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta,
9570ef204   Magnus Damm   clocksource: Supe...
195
196
197
  			    int periodic)
  {
  	/* stop timer */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
198
  	sh_tmu_start_stop_ch(ch, 0);
9570ef204   Magnus Damm   clocksource: Supe...
199
200
  
  	/* acknowledge interrupt */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
201
  	sh_tmu_read(ch, TCR);
9570ef204   Magnus Damm   clocksource: Supe...
202
203
  
  	/* enable interrupt */
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
204
  	sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
9570ef204   Magnus Damm   clocksource: Supe...
205
206
207
  
  	/* reload delta value in case of periodic timer */
  	if (periodic)
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
208
  		sh_tmu_write(ch, TCOR, delta);
9570ef204   Magnus Damm   clocksource: Supe...
209
  	else
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
210
  		sh_tmu_write(ch, TCOR, 0xffffffff);
9570ef204   Magnus Damm   clocksource: Supe...
211

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
212
  	sh_tmu_write(ch, TCNT, delta);
9570ef204   Magnus Damm   clocksource: Supe...
213
214
  
  	/* start timer */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
215
  	sh_tmu_start_stop_ch(ch, 1);
9570ef204   Magnus Damm   clocksource: Supe...
216
217
218
219
  }
  
  static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
220
  	struct sh_tmu_channel *ch = dev_id;
9570ef204   Magnus Damm   clocksource: Supe...
221
222
  
  	/* disable or acknowledge interrupt */
2bcc4da3d   Viresh Kumar   clockevents/drive...
223
  	if (clockevent_state_oneshot(&ch->ced))
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
224
  		sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
9570ef204   Magnus Damm   clocksource: Supe...
225
  	else
5cfe2d151   Laurent Pinchart   clocksource: sh_t...
226
  		sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
9570ef204   Magnus Damm   clocksource: Supe...
227
228
  
  	/* notify clockevent layer */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
229
  	ch->ced.event_handler(&ch->ced);
9570ef204   Magnus Damm   clocksource: Supe...
230
231
  	return IRQ_HANDLED;
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
232
  static struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs)
9570ef204   Magnus Damm   clocksource: Supe...
233
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
234
  	return container_of(cs, struct sh_tmu_channel, cs);
9570ef204   Magnus Damm   clocksource: Supe...
235
  }
a5a1d1c29   Thomas Gleixner   clocksource: Use ...
236
  static u64 sh_tmu_clocksource_read(struct clocksource *cs)
9570ef204   Magnus Damm   clocksource: Supe...
237
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
238
  	struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
9570ef204   Magnus Damm   clocksource: Supe...
239

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
240
  	return sh_tmu_read(ch, TCNT) ^ 0xffffffff;
9570ef204   Magnus Damm   clocksource: Supe...
241
242
243
244
  }
  
  static int sh_tmu_clocksource_enable(struct clocksource *cs)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
245
  	struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
0aeac458d   Magnus Damm   clocksource: sh_t...
246
  	int ret;
9570ef204   Magnus Damm   clocksource: Supe...
247

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
248
  	if (WARN_ON(ch->cs_enabled))
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
249
  		return 0;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
250
  	ret = sh_tmu_enable(ch);
c3c0a20df   Nicolai Stange   clocksource: sh_t...
251
  	if (!ret)
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
252
  		ch->cs_enabled = true;
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
253

0aeac458d   Magnus Damm   clocksource: sh_t...
254
  	return ret;
9570ef204   Magnus Damm   clocksource: Supe...
255
256
257
258
  }
  
  static void sh_tmu_clocksource_disable(struct clocksource *cs)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
259
  	struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
260

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
261
  	if (WARN_ON(!ch->cs_enabled))
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
262
  		return;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
263

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
264
265
  	sh_tmu_disable(ch);
  	ch->cs_enabled = false;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
266
267
268
269
  }
  
  static void sh_tmu_clocksource_suspend(struct clocksource *cs)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
270
  	struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
271

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
272
  	if (!ch->cs_enabled)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
273
  		return;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
274

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
275
276
277
  	if (--ch->enable_count == 0) {
  		__sh_tmu_disable(ch);
  		pm_genpd_syscore_poweroff(&ch->tmu->pdev->dev);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
278
  	}
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
279
280
281
282
  }
  
  static void sh_tmu_clocksource_resume(struct clocksource *cs)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
283
  	struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
284

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
285
  	if (!ch->cs_enabled)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
286
  		return;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
287
288
289
  	if (ch->enable_count++ == 0) {
  		pm_genpd_syscore_poweron(&ch->tmu->pdev->dev);
  		__sh_tmu_enable(ch);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
290
  	}
9570ef204   Magnus Damm   clocksource: Supe...
291
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
292
  static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch,
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
293
  				       const char *name)
9570ef204   Magnus Damm   clocksource: Supe...
294
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
295
  	struct clocksource *cs = &ch->cs;
9570ef204   Magnus Damm   clocksource: Supe...
296
297
  
  	cs->name = name;
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
298
  	cs->rating = 200;
9570ef204   Magnus Damm   clocksource: Supe...
299
300
301
  	cs->read = sh_tmu_clocksource_read;
  	cs->enable = sh_tmu_clocksource_enable;
  	cs->disable = sh_tmu_clocksource_disable;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
302
303
  	cs->suspend = sh_tmu_clocksource_suspend;
  	cs->resume = sh_tmu_clocksource_resume;
9570ef204   Magnus Damm   clocksource: Supe...
304
305
  	cs->mask = CLOCKSOURCE_MASK(32);
  	cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
66f49121f   Aurelien Jarno   clocksource: sh_t...
306

fe68eb802   Laurent Pinchart   clocksource: sh_t...
307
308
309
  	dev_info(&ch->tmu->pdev->dev, "ch%u: used as clock source
  ",
  		 ch->index);
0aeac458d   Magnus Damm   clocksource: sh_t...
310

c3c0a20df   Nicolai Stange   clocksource: sh_t...
311
  	clocksource_register_hz(cs, ch->tmu->rate);
9570ef204   Magnus Damm   clocksource: Supe...
312
313
  	return 0;
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
314
  static struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced)
9570ef204   Magnus Damm   clocksource: Supe...
315
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
316
  	return container_of(ced, struct sh_tmu_channel, ced);
9570ef204   Magnus Damm   clocksource: Supe...
317
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
318
  static void sh_tmu_clock_event_start(struct sh_tmu_channel *ch, int periodic)
9570ef204   Magnus Damm   clocksource: Supe...
319
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
320
  	sh_tmu_enable(ch);
9570ef204   Magnus Damm   clocksource: Supe...
321

9570ef204   Magnus Damm   clocksource: Supe...
322
  	if (periodic) {
c3c0a20df   Nicolai Stange   clocksource: sh_t...
323
  		ch->periodic = (ch->tmu->rate + HZ/2) / HZ;
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
324
  		sh_tmu_set_next(ch, ch->periodic, 1);
9570ef204   Magnus Damm   clocksource: Supe...
325
326
  	}
  }
2bcc4da3d   Viresh Kumar   clockevents/drive...
327
328
329
  static int sh_tmu_clock_event_shutdown(struct clock_event_device *ced)
  {
  	struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
452b13248   Viresh Kumar   clocksource/drive...
330
331
  	if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
  		sh_tmu_disable(ch);
2bcc4da3d   Viresh Kumar   clockevents/drive...
332
333
334
335
336
  	return 0;
  }
  
  static int sh_tmu_clock_event_set_state(struct clock_event_device *ced,
  					int periodic)
9570ef204   Magnus Damm   clocksource: Supe...
337
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
338
  	struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
9570ef204   Magnus Damm   clocksource: Supe...
339
340
  
  	/* deal with old setting first */
2bcc4da3d   Viresh Kumar   clockevents/drive...
341
  	if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
342
  		sh_tmu_disable(ch);
9570ef204   Magnus Damm   clocksource: Supe...
343

2bcc4da3d   Viresh Kumar   clockevents/drive...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  	dev_info(&ch->tmu->pdev->dev, "ch%u: used for %s clock events
  ",
  		 ch->index, periodic ? "periodic" : "oneshot");
  	sh_tmu_clock_event_start(ch, periodic);
  	return 0;
  }
  
  static int sh_tmu_clock_event_set_oneshot(struct clock_event_device *ced)
  {
  	return sh_tmu_clock_event_set_state(ced, 0);
  }
  
  static int sh_tmu_clock_event_set_periodic(struct clock_event_device *ced)
  {
  	return sh_tmu_clock_event_set_state(ced, 1);
9570ef204   Magnus Damm   clocksource: Supe...
359
360
361
362
363
  }
  
  static int sh_tmu_clock_event_next(unsigned long delta,
  				   struct clock_event_device *ced)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
364
  	struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
9570ef204   Magnus Damm   clocksource: Supe...
365

2bcc4da3d   Viresh Kumar   clockevents/drive...
366
  	BUG_ON(!clockevent_state_oneshot(ced));
9570ef204   Magnus Damm   clocksource: Supe...
367
368
  
  	/* program new delta value */
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
369
  	sh_tmu_set_next(ch, delta, 0);
9570ef204   Magnus Damm   clocksource: Supe...
370
371
  	return 0;
  }
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
372
373
  static void sh_tmu_clock_event_suspend(struct clock_event_device *ced)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
374
  	pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
375
376
377
378
  }
  
  static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
379
  	pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
380
  }
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
381
  static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
382
  				       const char *name)
9570ef204   Magnus Damm   clocksource: Supe...
383
  {
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
384
  	struct clock_event_device *ced = &ch->ced;
9570ef204   Magnus Damm   clocksource: Supe...
385
  	int ret;
9570ef204   Magnus Damm   clocksource: Supe...
386
387
388
  	ced->name = name;
  	ced->features = CLOCK_EVT_FEAT_PERIODIC;
  	ced->features |= CLOCK_EVT_FEAT_ONESHOT;
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
389
  	ced->rating = 200;
f2a547386   Magnus Damm   clocksource: sh_t...
390
  	ced->cpumask = cpu_possible_mask;
9570ef204   Magnus Damm   clocksource: Supe...
391
  	ced->set_next_event = sh_tmu_clock_event_next;
2bcc4da3d   Viresh Kumar   clockevents/drive...
392
393
394
  	ced->set_state_shutdown = sh_tmu_clock_event_shutdown;
  	ced->set_state_periodic = sh_tmu_clock_event_set_periodic;
  	ced->set_state_oneshot = sh_tmu_clock_event_set_oneshot;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
395
396
  	ced->suspend = sh_tmu_clock_event_suspend;
  	ced->resume = sh_tmu_clock_event_resume;
9570ef204   Magnus Damm   clocksource: Supe...
397

fe68eb802   Laurent Pinchart   clocksource: sh_t...
398
399
400
  	dev_info(&ch->tmu->pdev->dev, "ch%u: used for clock events
  ",
  		 ch->index);
3977407e8   Paul Mundt   clocksource: sh_t...
401

c3c0a20df   Nicolai Stange   clocksource: sh_t...
402
  	clockevents_config_and_register(ced, ch->tmu->rate, 0x300, 0xffffffff);
da64c2a8d   Paul Mundt   clocksource: Fix ...
403

de2d12c7e   Laurent Pinchart   clocksource: sh_t...
404
  	ret = request_irq(ch->irq, sh_tmu_interrupt,
1c56cf6b0   Laurent Pinchart   clocksource: sh_t...
405
  			  IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
de2d12c7e   Laurent Pinchart   clocksource: sh_t...
406
  			  dev_name(&ch->tmu->pdev->dev), ch);
9570ef204   Magnus Damm   clocksource: Supe...
407
  	if (ret) {
fe68eb802   Laurent Pinchart   clocksource: sh_t...
408
409
410
  		dev_err(&ch->tmu->pdev->dev, "ch%u: failed to request irq %d
  ",
  			ch->index, ch->irq);
9570ef204   Magnus Damm   clocksource: Supe...
411
412
  		return;
  	}
9570ef204   Magnus Damm   clocksource: Supe...
413
  }
84876d050   Laurent Pinchart   clocksource: sh_t...
414
  static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name,
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
415
  			   bool clockevent, bool clocksource)
9570ef204   Magnus Damm   clocksource: Supe...
416
  {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
417
418
  	if (clockevent) {
  		ch->tmu->has_clockevent = true;
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
419
  		sh_tmu_register_clockevent(ch, name);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
420
421
  	} else if (clocksource) {
  		ch->tmu->has_clocksource = true;
f1010ed1a   Laurent Pinchart   clocksource: sh_t...
422
  		sh_tmu_register_clocksource(ch, name);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
423
  	}
9570ef204   Magnus Damm   clocksource: Supe...
424
425
426
  
  	return 0;
  }
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
427
428
  static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
  				bool clockevent, bool clocksource,
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
429
430
  				struct sh_tmu_device *tmu)
  {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
431
432
433
  	/* Skip unused channels. */
  	if (!clockevent && !clocksource)
  		return 0;
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
434

a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
435
  	ch->tmu = tmu;
681b9e852   Laurent Pinchart   clocksource: sh_t...
436
  	ch->index = index;
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
437

681b9e852   Laurent Pinchart   clocksource: sh_t...
438
439
440
441
  	if (tmu->model == SH_TMU_SH3)
  		ch->base = tmu->mapbase + 4 + ch->index * 12;
  	else
  		ch->base = tmu->mapbase + 8 + ch->index * 12;
fe68eb802   Laurent Pinchart   clocksource: sh_t...
442

c54697ae1   Laurent Pinchart   clocksource: sh_t...
443
  	ch->irq = platform_get_irq(tmu->pdev, index);
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
444
  	if (ch->irq < 0) {
fe68eb802   Laurent Pinchart   clocksource: sh_t...
445
446
447
  		dev_err(&tmu->pdev->dev, "ch%u: failed to get irq
  ",
  			ch->index);
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
448
449
450
451
452
  		return ch->irq;
  	}
  
  	ch->cs_enabled = false;
  	ch->enable_count = 0;
84876d050   Laurent Pinchart   clocksource: sh_t...
453
  	return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
454
  			       clockevent, clocksource);
a94ddaa6f   Laurent Pinchart   clocksource: sh_t...
455
  }
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
456
  static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
9570ef204   Magnus Damm   clocksource: Supe...
457
  {
9570ef204   Magnus Damm   clocksource: Supe...
458
  	struct resource *res;
9570ef204   Magnus Damm   clocksource: Supe...
459

0a72aa39c   Laurent Pinchart   clocksource: sh_t...
460
  	res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
9570ef204   Magnus Damm   clocksource: Supe...
461
  	if (!res) {
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
462
463
  		dev_err(&tmu->pdev->dev, "failed to get I/O memory
  ");
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
464
  		return -ENXIO;
9570ef204   Magnus Damm   clocksource: Supe...
465
  	}
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
466
467
468
  	tmu->mapbase = ioremap_nocache(res->start, resource_size(res));
  	if (tmu->mapbase == NULL)
  		return -ENXIO;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
469
470
  	return 0;
  }
de693461b   Laurent Pinchart   clocksource: sh_t...
471

3e29b5543   Laurent Pinchart   clocksource: sh_t...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
  {
  	struct device_node *np = tmu->pdev->dev.of_node;
  
  	tmu->model = SH_TMU;
  	tmu->num_channels = 3;
  
  	of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);
  
  	if (tmu->num_channels != 2 && tmu->num_channels != 3) {
  		dev_err(&tmu->pdev->dev, "invalid number of channels %u
  ",
  			tmu->num_channels);
  		return -EINVAL;
  	}
  
  	return 0;
  }
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
490
491
  static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
  {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
492
493
  	unsigned int i;
  	int ret;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
494
  	tmu->pdev = pdev;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
495

2b027f1f0   Laurent Pinchart   clocksource: sh_t...
496
  	raw_spin_lock_init(&tmu->lock);
3e29b5543   Laurent Pinchart   clocksource: sh_t...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
  		ret = sh_tmu_parse_dt(tmu);
  		if (ret < 0)
  			return ret;
  	} else if (pdev->dev.platform_data) {
  		const struct platform_device_id *id = pdev->id_entry;
  		struct sh_timer_config *cfg = pdev->dev.platform_data;
  
  		tmu->model = id->driver_data;
  		tmu->num_channels = hweight8(cfg->channels_mask);
  	} else {
  		dev_err(&tmu->pdev->dev, "missing platform data
  ");
  		return -ENXIO;
  	}
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
512
  	/* Get hold of clock. */
681b9e852   Laurent Pinchart   clocksource: sh_t...
513
  	tmu->clk = clk_get(&tmu->pdev->dev, "fck");
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
514
515
516
  	if (IS_ERR(tmu->clk)) {
  		dev_err(&tmu->pdev->dev, "cannot get clock
  ");
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
517
  		return PTR_ERR(tmu->clk);
9570ef204   Magnus Damm   clocksource: Supe...
518
  	}
1c09eb3e2   Laurent Pinchart   clocksource: sh_t...
519

0a72aa39c   Laurent Pinchart   clocksource: sh_t...
520
  	ret = clk_prepare(tmu->clk);
1c09eb3e2   Laurent Pinchart   clocksource: sh_t...
521
  	if (ret < 0)
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
522
  		goto err_clk_put;
c3c0a20df   Nicolai Stange   clocksource: sh_t...
523
524
525
526
527
528
529
  	/* Determine clock rate. */
  	ret = clk_enable(tmu->clk);
  	if (ret < 0)
  		goto err_clk_unprepare;
  
  	tmu->rate = clk_get_rate(tmu->clk) / 4;
  	clk_disable(tmu->clk);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
530
531
532
533
534
535
536
  	/* Map the memory resource. */
  	ret = sh_tmu_map_memory(tmu);
  	if (ret < 0) {
  		dev_err(&tmu->pdev->dev, "failed to remap I/O memory
  ");
  		goto err_clk_unprepare;
  	}
1c09eb3e2   Laurent Pinchart   clocksource: sh_t...
537

8c7f21e67   Laurent Pinchart   clocksource: sh_t...
538
  	/* Allocate and setup the channels. */
6396bb221   Kees Cook   treewide: kzalloc...
539
  	tmu->channels = kcalloc(tmu->num_channels, sizeof(*tmu->channels),
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
540
  				GFP_KERNEL);
a5de49f43   Laurent Pinchart   clocksource: sh_t...
541
542
  	if (tmu->channels == NULL) {
  		ret = -ENOMEM;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
543
  		goto err_unmap;
a5de49f43   Laurent Pinchart   clocksource: sh_t...
544
  	}
681b9e852   Laurent Pinchart   clocksource: sh_t...
545
546
547
548
549
550
551
  	/*
  	 * Use the first channel as a clock event device and the second channel
  	 * as a clock source.
  	 */
  	for (i = 0; i < tmu->num_channels; ++i) {
  		ret = sh_tmu_channel_setup(&tmu->channels[i], i,
  					   i == 0, i == 1, tmu);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
552
553
  		if (ret < 0)
  			goto err_unmap;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
554
  	}
a5de49f43   Laurent Pinchart   clocksource: sh_t...
555

8c7f21e67   Laurent Pinchart   clocksource: sh_t...
556
  	platform_set_drvdata(pdev, tmu);
394a4486f   Laurent Pinchart   clocksource: sh_t...
557
558
  
  	return 0;
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
559
  err_unmap:
a5de49f43   Laurent Pinchart   clocksource: sh_t...
560
  	kfree(tmu->channels);
681b9e852   Laurent Pinchart   clocksource: sh_t...
561
  	iounmap(tmu->mapbase);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
562
  err_clk_unprepare:
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
563
  	clk_unprepare(tmu->clk);
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
564
  err_clk_put:
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
565
  	clk_put(tmu->clk);
9570ef204   Magnus Damm   clocksource: Supe...
566
567
  	return ret;
  }
1850514b3   Greg Kroah-Hartman   Drivers: clocksou...
568
  static int sh_tmu_probe(struct platform_device *pdev)
9570ef204   Magnus Damm   clocksource: Supe...
569
  {
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
570
  	struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
9570ef204   Magnus Damm   clocksource: Supe...
571
  	int ret;
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
572
  	if (!is_early_platform_device(pdev)) {
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
573
574
  		pm_runtime_set_active(&pdev->dev);
  		pm_runtime_enable(&pdev->dev);
eaa49a8cd   Rafael J. Wysocki   sh: TMU: Introduc...
575
  	}
2ee619f94   Rafael J. Wysocki   PM / shmobile: Ma...
576

0a72aa39c   Laurent Pinchart   clocksource: sh_t...
577
  	if (tmu) {
214a607a4   Paul Mundt   clocksource: Use ...
578
579
  		dev_info(&pdev->dev, "kept as earlytimer
  ");
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
580
  		goto out;
9570ef204   Magnus Damm   clocksource: Supe...
581
  	}
3b77a83ee   Laurent Pinchart   clocksource: sh_t...
582
  	tmu = kzalloc(sizeof(*tmu), GFP_KERNEL);
814876b0b   Jingoo Han   clocksource: sh_t...
583
  	if (tmu == NULL)
9570ef204   Magnus Damm   clocksource: Supe...
584
  		return -ENOMEM;
9570ef204   Magnus Damm   clocksource: Supe...
585

0a72aa39c   Laurent Pinchart   clocksource: sh_t...
586
  	ret = sh_tmu_setup(tmu, pdev);
9570ef204   Magnus Damm   clocksource: Supe...
587
  	if (ret) {
0a72aa39c   Laurent Pinchart   clocksource: sh_t...
588
  		kfree(tmu);
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
589
590
  		pm_runtime_idle(&pdev->dev);
  		return ret;
9570ef204   Magnus Damm   clocksource: Supe...
591
  	}
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
592
593
594
595
  	if (is_early_platform_device(pdev))
  		return 0;
  
   out:
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
596
  	if (tmu->has_clockevent || tmu->has_clocksource)
61a53bfaa   Rafael J. Wysocki   sh: TMU: Basic ru...
597
598
599
600
601
  		pm_runtime_irq_safe(&pdev->dev);
  	else
  		pm_runtime_idle(&pdev->dev);
  
  	return 0;
9570ef204   Magnus Damm   clocksource: Supe...
602
  }
1850514b3   Greg Kroah-Hartman   Drivers: clocksou...
603
  static int sh_tmu_remove(struct platform_device *pdev)
9570ef204   Magnus Damm   clocksource: Supe...
604
605
606
  {
  	return -EBUSY; /* cannot unregister clockevent and clocksource */
  }
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
607
  static const struct platform_device_id sh_tmu_id_table[] = {
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
608
609
610
611
612
  	{ "sh-tmu", SH_TMU },
  	{ "sh-tmu-sh3", SH_TMU_SH3 },
  	{ }
  };
  MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
3e29b5543   Laurent Pinchart   clocksource: sh_t...
613
614
615
616
617
  static const struct of_device_id sh_tmu_of_table[] __maybe_unused = {
  	{ .compatible = "renesas,tmu" },
  	{ }
  };
  MODULE_DEVICE_TABLE(of, sh_tmu_of_table);
9570ef204   Magnus Damm   clocksource: Supe...
618
619
  static struct platform_driver sh_tmu_device_driver = {
  	.probe		= sh_tmu_probe,
1850514b3   Greg Kroah-Hartman   Drivers: clocksou...
620
  	.remove		= sh_tmu_remove,
9570ef204   Magnus Damm   clocksource: Supe...
621
622
  	.driver		= {
  		.name	= "sh_tmu",
3e29b5543   Laurent Pinchart   clocksource: sh_t...
623
  		.of_match_table = of_match_ptr(sh_tmu_of_table),
8c7f21e67   Laurent Pinchart   clocksource: sh_t...
624
625
  	},
  	.id_table	= sh_tmu_id_table,
9570ef204   Magnus Damm   clocksource: Supe...
626
627
628
629
630
631
632
633
634
635
636
637
638
  };
  
  static int __init sh_tmu_init(void)
  {
  	return platform_driver_register(&sh_tmu_device_driver);
  }
  
  static void __exit sh_tmu_exit(void)
  {
  	platform_driver_unregister(&sh_tmu_device_driver);
  }
  
  early_platform_init("earlytimer", &sh_tmu_device_driver);
b9773c3f5   Simon Horman   clocksource: sh_t...
639
  subsys_initcall(sh_tmu_init);
9570ef204   Magnus Damm   clocksource: Supe...
640
641
642
643
644
  module_exit(sh_tmu_exit);
  
  MODULE_AUTHOR("Magnus Damm");
  MODULE_DESCRIPTION("SuperH TMU Timer Driver");
  MODULE_LICENSE("GPL v2");