2 * linux/arch/h8300/kernel/cpu/timer/timer8.c
4 * Yoshinori Sato <ysato@users.sourcefoge.jp>
10 #include <linux/errno.h>
11 #include <linux/sched.h>
12 #include <linux/kernel.h>
13 #include <linux/interrupt.h>
14 #include <linux/init.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 #include <linux/clockchips.h>
18 #include <linux/module.h>
19 #include <linux/clk.h>
31 #define FLAG_REPROGRAM (1 << 0)
32 #define FLAG_SKIPEVENT (1 << 1)
33 #define FLAG_IRQCONTEXT (1 << 2)
34 #define FLAG_STARTED (1 << 3)
43 struct platform_device
*pdev
;
44 struct clock_event_device ced
;
45 struct irqaction irqaction
;
46 unsigned long mapbase
;
54 static unsigned long timer8_get_counter(struct timer8_priv
*p
)
56 unsigned long v1
, v2
, v3
;
59 o1
= ctrl_inb(p
->mapbase
+ _8TCSR
) & 0x20;
61 /* Make sure the timer value is stable. Stolen from acpi_pm.c */
64 v1
= ctrl_inw(p
->mapbase
+ _8TCNT
);
65 v2
= ctrl_inw(p
->mapbase
+ _8TCNT
);
66 v3
= ctrl_inw(p
->mapbase
+ _8TCNT
);
67 o1
= ctrl_inb(p
->mapbase
+ _8TCSR
) & 0x20;
68 } while (unlikely((o1
!= o2
) || (v1
> v2
&& v1
< v3
)
69 || (v2
> v3
&& v2
< v1
) || (v3
> v1
&& v3
< v2
)));
75 static irqreturn_t
timer8_interrupt(int irq
, void *dev_id
)
77 struct timer8_priv
*p
= dev_id
;
79 ctrl_outb(ctrl_inb(p
->mapbase
+ _8TCSR
) & ~0x40,
81 p
->flags
|= FLAG_IRQCONTEXT
;
82 ctrl_outw(p
->tcora
, p
->mapbase
+ TCORA
);
83 if (!(p
->flags
& FLAG_SKIPEVENT
)) {
84 if (p
->ced
.mode
== CLOCK_EVT_MODE_ONESHOT
)
85 ctrl_outw(0x0000, p
->mapbase
+ _8TCR
);
86 p
->ced
.event_handler(&p
->ced
);
88 p
->flags
&= ~(FLAG_SKIPEVENT
| FLAG_IRQCONTEXT
);
93 static void timer8_set_next(struct timer8_priv
*p
, unsigned long delta
)
98 raw_spin_lock_irqsave(&p
->lock
, flags
);
100 dev_warn(&p
->pdev
->dev
, "delta out of range\n");
101 now
= timer8_get_counter(p
);
103 ctrl_outb(ctrl_inb(p
->mapbase
+ _8TCR
) | 0x40, p
->mapbase
+ _8TCR
);
105 ctrl_outw(delta
, p
->mapbase
+ TCORA
);
107 ctrl_outw(now
+ 1, p
->mapbase
+ TCORA
);
109 raw_spin_unlock_irqrestore(&p
->lock
, flags
);
112 static int timer8_enable(struct timer8_priv
*p
)
114 p
->rate
= clk_get_rate(p
->pclk
) / 64;
115 ctrl_outw(0xffff, p
->mapbase
+ TCORA
);
116 ctrl_outw(0x0000, p
->mapbase
+ _8TCNT
);
117 ctrl_outw(0x0c02, p
->mapbase
+ _8TCR
);
122 static int timer8_start(struct timer8_priv
*p
)
127 raw_spin_lock_irqsave(&p
->lock
, flags
);
129 if (!(p
->flags
& FLAG_STARTED
))
130 ret
= timer8_enable(p
);
134 p
->flags
|= FLAG_STARTED
;
137 raw_spin_unlock_irqrestore(&p
->lock
, flags
);
142 static void timer8_stop(struct timer8_priv
*p
)
146 raw_spin_lock_irqsave(&p
->lock
, flags
);
148 ctrl_outw(0x0000, p
->mapbase
+ _8TCR
);
150 raw_spin_unlock_irqrestore(&p
->lock
, flags
);
153 static inline struct timer8_priv
*ced_to_priv(struct clock_event_device
*ced
)
155 return container_of(ced
, struct timer8_priv
, ced
);
158 static void timer8_clock_event_start(struct timer8_priv
*p
, int periodic
)
160 struct clock_event_device
*ced
= &p
->ced
;
165 ced
->mult
= div_sc(p
->rate
, NSEC_PER_SEC
, ced
->shift
);
166 ced
->max_delta_ns
= clockevent_delta2ns(0xffff, ced
);
167 ced
->min_delta_ns
= clockevent_delta2ns(0x0001, ced
);
169 timer8_set_next(p
, periodic
?(p
->rate
+ HZ
/2) / HZ
:0x10000);
172 static void timer8_clock_event_mode(enum clock_event_mode mode
,
173 struct clock_event_device
*ced
)
175 struct timer8_priv
*p
= ced_to_priv(ced
);
178 case CLOCK_EVT_MODE_PERIODIC
:
179 dev_info(&p
->pdev
->dev
, "used for periodic clock events\n");
181 timer8_clock_event_start(p
, PERIODIC
);
183 case CLOCK_EVT_MODE_ONESHOT
:
184 dev_info(&p
->pdev
->dev
, "used for oneshot clock events\n");
186 timer8_clock_event_start(p
, ONESHOT
);
188 case CLOCK_EVT_MODE_SHUTDOWN
:
189 case CLOCK_EVT_MODE_UNUSED
:
197 static int timer8_clock_event_next(unsigned long delta
,
198 struct clock_event_device
*ced
)
200 struct timer8_priv
*p
= ced_to_priv(ced
);
202 BUG_ON(ced
->mode
!= CLOCK_EVT_MODE_ONESHOT
);
203 timer8_set_next(p
, delta
- 1);
208 static int timer8_setup(struct timer8_priv
*p
,
209 struct platform_device
*pdev
)
211 struct resource
*res
;
215 memset(p
, 0, sizeof(*p
));
218 res
= platform_get_resource(p
->pdev
, IORESOURCE_MEM
, 0);
220 dev_err(&p
->pdev
->dev
, "failed to get I/O memory\n");
224 irq
= platform_get_irq(p
->pdev
, 0);
226 dev_err(&p
->pdev
->dev
, "failed to get irq\n");
230 p
->mapbase
= res
->start
;
232 p
->irqaction
.name
= dev_name(&p
->pdev
->dev
);
233 p
->irqaction
.handler
= timer8_interrupt
;
234 p
->irqaction
.dev_id
= p
;
235 p
->irqaction
.flags
= IRQF_TIMER
;
237 p
->pclk
= clk_get(&p
->pdev
->dev
, "fck");
238 if (IS_ERR(p
->pclk
)) {
239 dev_err(&p
->pdev
->dev
, "can't get clk\n");
240 return PTR_ERR(p
->pclk
);
243 p
->ced
.name
= pdev
->name
;
244 p
->ced
.features
= CLOCK_EVT_FEAT_PERIODIC
|
245 CLOCK_EVT_FEAT_ONESHOT
;
247 p
->ced
.cpumask
= cpumask_of(0);
248 p
->ced
.set_next_event
= timer8_clock_event_next
;
249 p
->ced
.set_mode
= timer8_clock_event_mode
;
251 ret
= setup_irq(irq
, &p
->irqaction
);
253 dev_err(&p
->pdev
->dev
,
254 "failed to request irq %d\n", irq
);
257 clockevents_register_device(&p
->ced
);
258 platform_set_drvdata(pdev
, p
);
263 static int timer8_probe(struct platform_device
*pdev
)
265 struct timer8_priv
*p
= platform_get_drvdata(pdev
);
268 dev_info(&pdev
->dev
, "kept as earlytimer\n");
272 p
= devm_kzalloc(&pdev
->dev
, sizeof(*p
), GFP_KERNEL
);
276 return timer8_setup(p
, pdev
);
279 static int timer8_remove(struct platform_device
*pdev
)
284 static const struct of_device_id timer8_of_table
[] __maybe_unused
= {
285 { .compatible
= "renesas,8bit-timer" },
289 MODULE_DEVICE_TABLE(of
, timer8_of_table
);
290 static struct platform_driver timer8_driver
= {
291 .probe
= timer8_probe
,
292 .remove
= timer8_remove
,
294 .name
= "h8300-8timer",
295 .of_match_table
= of_match_ptr(timer8_of_table
),
299 static int __init
timer8_init(void)
301 return platform_driver_register(&timer8_driver
);
304 static void __exit
timer8_exit(void)
306 platform_driver_unregister(&timer8_driver
);
309 subsys_initcall(timer8_init
);
310 module_exit(timer8_exit
);
311 MODULE_AUTHOR("Yoshinori Sato");
312 MODULE_DESCRIPTION("H8/300 8bit Timer Driver");
313 MODULE_LICENSE("GPL v2");