]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
618b902d YS |
2 | /* |
3 | * linux/arch/h8300/kernel/cpu/timer/timer8.c | |
4 | * | |
5 | * Yoshinori Sato <ysato@users.sourcefoge.jp> | |
6 | * | |
7 | * 8bit Timer driver | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/errno.h> | |
618b902d YS |
12 | #include <linux/kernel.h> |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/init.h> | |
618b902d | 15 | #include <linux/clockchips.h> |
618b902d YS |
16 | #include <linux/clk.h> |
17 | #include <linux/io.h> | |
18 | #include <linux/of.h> | |
4633f4ca YS |
19 | #include <linux/of_address.h> |
20 | #include <linux/of_irq.h> | |
618b902d | 21 | |
618b902d YS |
22 | #define _8TCR 0 |
23 | #define _8TCSR 2 | |
24 | #define TCORA 4 | |
25 | #define TCORB 6 | |
26 | #define _8TCNT 8 | |
27 | ||
d33f250a YS |
28 | #define CMIEA 6 |
29 | #define CMFA 6 | |
30 | ||
618b902d YS |
31 | #define FLAG_STARTED (1 << 3) |
32 | ||
4633f4ca YS |
33 | #define SCALE 64 |
34 | ||
d33f250a YS |
35 | #define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a)) |
36 | #define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a)) | |
37 | ||
618b902d | 38 | struct timer8_priv { |
618b902d | 39 | struct clock_event_device ced; |
75160515 | 40 | void __iomem *mapbase; |
618b902d YS |
41 | unsigned long flags; |
42 | unsigned int rate; | |
618b902d YS |
43 | }; |
44 | ||
618b902d YS |
45 | static irqreturn_t timer8_interrupt(int irq, void *dev_id) |
46 | { | |
47 | struct timer8_priv *p = dev_id; | |
48 | ||
7053fdac | 49 | if (clockevent_state_oneshot(&p->ced)) |
d33f250a | 50 | iowrite16be(0x0000, p->mapbase + _8TCR); |
7053fdac DL |
51 | |
52 | p->ced.event_handler(&p->ced); | |
618b902d | 53 | |
d33f250a | 54 | bclr(CMFA, p->mapbase + _8TCSR); |
f37632d1 | 55 | |
618b902d YS |
56 | return IRQ_HANDLED; |
57 | } | |
58 | ||
59 | static void timer8_set_next(struct timer8_priv *p, unsigned long delta) | |
60 | { | |
618b902d | 61 | if (delta >= 0x10000) |
8c09b7d6 | 62 | pr_warn("delta out of range\n"); |
d33f250a YS |
63 | bclr(CMIEA, p->mapbase + _8TCR); |
64 | iowrite16be(delta, p->mapbase + TCORA); | |
65 | iowrite16be(0x0000, p->mapbase + _8TCNT); | |
66 | bclr(CMFA, p->mapbase + _8TCSR); | |
67 | bset(CMIEA, p->mapbase + _8TCR); | |
618b902d YS |
68 | } |
69 | ||
70 | static int timer8_enable(struct timer8_priv *p) | |
71 | { | |
d33f250a YS |
72 | iowrite16be(0xffff, p->mapbase + TCORA); |
73 | iowrite16be(0x0000, p->mapbase + _8TCNT); | |
74 | iowrite16be(0x0c02, p->mapbase + _8TCR); | |
618b902d YS |
75 | |
76 | return 0; | |
77 | } | |
78 | ||
79 | static int timer8_start(struct timer8_priv *p) | |
80 | { | |
cce483e0 | 81 | int ret; |
618b902d | 82 | |
cce483e0 DL |
83 | if ((p->flags & FLAG_STARTED)) |
84 | return 0; | |
618b902d | 85 | |
cce483e0 DL |
86 | ret = timer8_enable(p); |
87 | if (!ret) | |
88 | p->flags |= FLAG_STARTED; | |
618b902d | 89 | |
618b902d YS |
90 | return ret; |
91 | } | |
92 | ||
93 | static void timer8_stop(struct timer8_priv *p) | |
94 | { | |
d33f250a | 95 | iowrite16be(0x0000, p->mapbase + _8TCR); |
618b902d YS |
96 | } |
97 | ||
98 | static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced) | |
99 | { | |
100 | return container_of(ced, struct timer8_priv, ced); | |
101 | } | |
102 | ||
1f058d52 | 103 | static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta) |
618b902d | 104 | { |
618b902d | 105 | timer8_start(p); |
1f058d52 | 106 | timer8_set_next(p, delta); |
618b902d YS |
107 | } |
108 | ||
fc2b2f5d VK |
109 | static int timer8_clock_event_shutdown(struct clock_event_device *ced) |
110 | { | |
111 | timer8_stop(ced_to_priv(ced)); | |
112 | return 0; | |
113 | } | |
114 | ||
115 | static int timer8_clock_event_periodic(struct clock_event_device *ced) | |
618b902d YS |
116 | { |
117 | struct timer8_priv *p = ced_to_priv(ced); | |
118 | ||
4633f4ca | 119 | pr_info("%s: used for periodic clock events\n", ced->name); |
fc2b2f5d | 120 | timer8_stop(p); |
1f058d52 | 121 | timer8_clock_event_start(p, (p->rate + HZ/2) / HZ); |
fc2b2f5d VK |
122 | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static int timer8_clock_event_oneshot(struct clock_event_device *ced) | |
127 | { | |
128 | struct timer8_priv *p = ced_to_priv(ced); | |
129 | ||
4633f4ca | 130 | pr_info("%s: used for oneshot clock events\n", ced->name); |
fc2b2f5d | 131 | timer8_stop(p); |
1f058d52 | 132 | timer8_clock_event_start(p, 0x10000); |
fc2b2f5d VK |
133 | |
134 | return 0; | |
618b902d YS |
135 | } |
136 | ||
137 | static int timer8_clock_event_next(unsigned long delta, | |
138 | struct clock_event_device *ced) | |
139 | { | |
140 | struct timer8_priv *p = ced_to_priv(ced); | |
141 | ||
fc2b2f5d | 142 | BUG_ON(!clockevent_state_oneshot(ced)); |
618b902d YS |
143 | timer8_set_next(p, delta - 1); |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
4633f4ca YS |
148 | static struct timer8_priv timer8_priv = { |
149 | .ced = { | |
150 | .name = "h8300_8timer", | |
151 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | |
152 | .rating = 200, | |
153 | .set_next_event = timer8_clock_event_next, | |
154 | .set_state_shutdown = timer8_clock_event_shutdown, | |
155 | .set_state_periodic = timer8_clock_event_periodic, | |
156 | .set_state_oneshot = timer8_clock_event_oneshot, | |
157 | }, | |
158 | }; | |
159 | ||
691f8f87 | 160 | static int __init h8300_8timer_init(struct device_node *node) |
618b902d | 161 | { |
4633f4ca | 162 | void __iomem *base; |
691f8f87 | 163 | int irq, ret; |
4633f4ca | 164 | struct clk *clk; |
618b902d | 165 | |
4633f4ca YS |
166 | clk = of_clk_get(node, 0); |
167 | if (IS_ERR(clk)) { | |
168 | pr_err("failed to get clock for clockevent\n"); | |
691f8f87 | 169 | return PTR_ERR(clk); |
4633f4ca | 170 | } |
618b902d | 171 | |
691f8f87 | 172 | ret = ENXIO; |
4633f4ca YS |
173 | base = of_iomap(node, 0); |
174 | if (!base) { | |
175 | pr_err("failed to map registers for clockevent\n"); | |
176 | goto free_clk; | |
618b902d YS |
177 | } |
178 | ||
691f8f87 | 179 | ret = -EINVAL; |
4633f4ca | 180 | irq = irq_of_parse_and_map(node, 0); |
54a0cd5a | 181 | if (!irq) { |
4633f4ca YS |
182 | pr_err("failed to get irq for clockevent\n"); |
183 | goto unmap_reg; | |
618b902d YS |
184 | } |
185 | ||
75160515 | 186 | timer8_priv.mapbase = base; |
cce483e0 | 187 | |
6f2b611d YS |
188 | timer8_priv.rate = clk_get_rate(clk) / SCALE; |
189 | if (!timer8_priv.rate) { | |
cce483e0 DL |
190 | pr_err("Failed to get rate for the clocksource\n"); |
191 | goto unmap_reg; | |
192 | } | |
618b902d | 193 | |
6f2b611d YS |
194 | if (request_irq(irq, timer8_interrupt, IRQF_TIMER, |
195 | timer8_priv.ced.name, &timer8_priv) < 0) { | |
4633f4ca YS |
196 | pr_err("failed to request irq %d for clockevent\n", irq); |
197 | goto unmap_reg; | |
618b902d | 198 | } |
cce483e0 | 199 | |
6f2b611d YS |
200 | clockevents_config_and_register(&timer8_priv.ced, |
201 | timer8_priv.rate, 1, 0x0000ffff); | |
4633f4ca | 202 | |
691f8f87 | 203 | return 0; |
4633f4ca YS |
204 | unmap_reg: |
205 | iounmap(base); | |
206 | free_clk: | |
207 | clk_put(clk); | |
691f8f87 | 208 | return ret; |
618b902d YS |
209 | } |
210 | ||
17273395 | 211 | TIMER_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init); |