Commit | Line | Data |
---|---|---|
274157a4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
fb6002a8 CB |
2 | /* |
3 | * Renesas Timer Support - OSTM | |
4 | * | |
5 | * Copyright (C) 2017 Renesas Electronics America, Inc. | |
6 | * Copyright (C) 2017 Chris Brandt | |
fb6002a8 CB |
7 | */ |
8 | ||
fb6002a8 CB |
9 | #include <linux/clk.h> |
10 | #include <linux/clockchips.h> | |
11 | #include <linux/interrupt.h> | |
3a3e9f23 BD |
12 | #include <linux/platform_device.h> |
13 | #include <linux/reset.h> | |
fb6002a8 CB |
14 | #include <linux/sched_clock.h> |
15 | #include <linux/slab.h> | |
16 | ||
22731423 GU |
17 | #include "timer-of.h" |
18 | ||
fb6002a8 CB |
19 | /* |
20 | * The OSTM contains independent channels. | |
21 | * The first OSTM channel probed will be set up as a free running | |
22 | * clocksource. Additionally we will use this clocksource for the system | |
23 | * schedule timer sched_clock(). | |
24 | * | |
25 | * The second (or more) channel probed will be set up as an interrupt | |
26 | * driven clock event. | |
27 | */ | |
28 | ||
fb6002a8 CB |
29 | static void __iomem *system_clock; /* For sched_clock() */ |
30 | ||
31 | /* OSTM REGISTERS */ | |
32 | #define OSTM_CMP 0x000 /* RW,32 */ | |
33 | #define OSTM_CNT 0x004 /* R,32 */ | |
34 | #define OSTM_TE 0x010 /* R,8 */ | |
35 | #define OSTM_TS 0x014 /* W,8 */ | |
36 | #define OSTM_TT 0x018 /* W,8 */ | |
37 | #define OSTM_CTL 0x020 /* RW,8 */ | |
38 | ||
39 | #define TE 0x01 | |
40 | #define TS 0x01 | |
41 | #define TT 0x01 | |
42 | #define CTL_PERIODIC 0x00 | |
43 | #define CTL_ONESHOT 0x02 | |
44 | #define CTL_FREERUN 0x02 | |
45 | ||
22731423 | 46 | static void ostm_timer_stop(struct timer_of *to) |
fb6002a8 | 47 | { |
22731423 GU |
48 | if (readb(timer_of_base(to) + OSTM_TE) & TE) { |
49 | writeb(TT, timer_of_base(to) + OSTM_TT); | |
fb6002a8 CB |
50 | |
51 | /* | |
52 | * Read back the register simply to confirm the write operation | |
53 | * has completed since I/O writes can sometimes get queued by | |
54 | * the bus architecture. | |
55 | */ | |
22731423 | 56 | while (readb(timer_of_base(to) + OSTM_TE) & TE) |
fb6002a8 CB |
57 | ; |
58 | } | |
59 | } | |
60 | ||
22731423 | 61 | static int __init ostm_init_clksrc(struct timer_of *to) |
fb6002a8 | 62 | { |
22731423 | 63 | ostm_timer_stop(to); |
fb6002a8 | 64 | |
22731423 GU |
65 | writel(0, timer_of_base(to) + OSTM_CMP); |
66 | writeb(CTL_FREERUN, timer_of_base(to) + OSTM_CTL); | |
67 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 | 68 | |
b35a5e59 GU |
69 | return clocksource_mmio_init(timer_of_base(to) + OSTM_CNT, |
70 | to->np->full_name, timer_of_rate(to), 300, | |
71 | 32, clocksource_mmio_readl_up); | |
fb6002a8 CB |
72 | } |
73 | ||
74 | static u64 notrace ostm_read_sched_clock(void) | |
75 | { | |
76 | return readl(system_clock); | |
77 | } | |
78 | ||
22731423 | 79 | static void __init ostm_init_sched_clock(struct timer_of *to) |
fb6002a8 | 80 | { |
22731423 GU |
81 | system_clock = timer_of_base(to) + OSTM_CNT; |
82 | sched_clock_register(ostm_read_sched_clock, 32, timer_of_rate(to)); | |
fb6002a8 CB |
83 | } |
84 | ||
85 | static int ostm_clock_event_next(unsigned long delta, | |
22731423 | 86 | struct clock_event_device *ced) |
fb6002a8 | 87 | { |
22731423 | 88 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 89 | |
22731423 | 90 | ostm_timer_stop(to); |
fb6002a8 | 91 | |
22731423 GU |
92 | writel(delta, timer_of_base(to) + OSTM_CMP); |
93 | writeb(CTL_ONESHOT, timer_of_base(to) + OSTM_CTL); | |
94 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 CB |
95 | |
96 | return 0; | |
97 | } | |
98 | ||
99 | static int ostm_shutdown(struct clock_event_device *ced) | |
100 | { | |
22731423 | 101 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 102 | |
22731423 | 103 | ostm_timer_stop(to); |
fb6002a8 CB |
104 | |
105 | return 0; | |
106 | } | |
107 | static int ostm_set_periodic(struct clock_event_device *ced) | |
108 | { | |
22731423 | 109 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 CB |
110 | |
111 | if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced)) | |
22731423 | 112 | ostm_timer_stop(to); |
fb6002a8 | 113 | |
22731423 GU |
114 | writel(timer_of_period(to) - 1, timer_of_base(to) + OSTM_CMP); |
115 | writeb(CTL_PERIODIC, timer_of_base(to) + OSTM_CTL); | |
116 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 CB |
117 | |
118 | return 0; | |
119 | } | |
120 | ||
121 | static int ostm_set_oneshot(struct clock_event_device *ced) | |
122 | { | |
22731423 | 123 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 124 | |
22731423 | 125 | ostm_timer_stop(to); |
fb6002a8 CB |
126 | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id) | |
131 | { | |
22731423 | 132 | struct clock_event_device *ced = dev_id; |
fb6002a8 | 133 | |
22731423 GU |
134 | if (clockevent_state_oneshot(ced)) |
135 | ostm_timer_stop(to_timer_of(ced)); | |
fb6002a8 CB |
136 | |
137 | /* notify clockevent layer */ | |
22731423 GU |
138 | if (ced->event_handler) |
139 | ced->event_handler(ced); | |
fb6002a8 CB |
140 | |
141 | return IRQ_HANDLED; | |
142 | } | |
143 | ||
22731423 | 144 | static int __init ostm_init_clkevt(struct timer_of *to) |
fb6002a8 | 145 | { |
22731423 | 146 | struct clock_event_device *ced = &to->clkevt; |
fb6002a8 | 147 | |
fb6002a8 CB |
148 | ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC; |
149 | ced->set_state_shutdown = ostm_shutdown; | |
150 | ced->set_state_periodic = ostm_set_periodic; | |
151 | ced->set_state_oneshot = ostm_set_oneshot; | |
152 | ced->set_next_event = ostm_clock_event_next; | |
153 | ced->shift = 32; | |
154 | ced->rating = 300; | |
155 | ced->cpumask = cpumask_of(0); | |
22731423 GU |
156 | clockevents_config_and_register(ced, timer_of_rate(to), 0xf, |
157 | 0xffffffff); | |
fb6002a8 CB |
158 | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static int __init ostm_init(struct device_node *np) | |
163 | { | |
3a3e9f23 | 164 | struct reset_control *rstc; |
22731423 GU |
165 | struct timer_of *to; |
166 | int ret; | |
fb6002a8 | 167 | |
22731423 GU |
168 | to = kzalloc(sizeof(*to), GFP_KERNEL); |
169 | if (!to) | |
170 | return -ENOMEM; | |
fb6002a8 | 171 | |
3a3e9f23 BD |
172 | rstc = of_reset_control_get_optional_exclusive(np, NULL); |
173 | if (IS_ERR(rstc)) { | |
174 | ret = PTR_ERR(rstc); | |
175 | goto err_free; | |
176 | } | |
177 | ||
178 | reset_control_deassert(rstc); | |
179 | ||
22731423 GU |
180 | to->flags = TIMER_OF_BASE | TIMER_OF_CLOCK; |
181 | if (system_clock) { | |
182 | /* | |
183 | * clock sources don't use interrupts, clock events do | |
184 | */ | |
185 | to->flags |= TIMER_OF_IRQ; | |
186 | to->of_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; | |
187 | to->of_irq.handler = ostm_timer_interrupt; | |
fb6002a8 CB |
188 | } |
189 | ||
22731423 GU |
190 | ret = timer_of_init(np, to); |
191 | if (ret) | |
3a3e9f23 | 192 | goto err_reset; |
fb6002a8 CB |
193 | |
194 | /* | |
195 | * First probed device will be used as system clocksource. Any | |
196 | * additional devices will be used as clock events. | |
197 | */ | |
198 | if (!system_clock) { | |
22731423 GU |
199 | ret = ostm_init_clksrc(to); |
200 | if (ret) | |
201 | goto err_cleanup; | |
fb6002a8 | 202 | |
22731423 | 203 | ostm_init_sched_clock(to); |
b35a5e59 | 204 | pr_info("%pOF: used for clocksource\n", np); |
fb6002a8 | 205 | } else { |
22731423 GU |
206 | ret = ostm_init_clkevt(to); |
207 | if (ret) | |
208 | goto err_cleanup; | |
fb6002a8 | 209 | |
b35a5e59 | 210 | pr_info("%pOF: used for clock events\n", np); |
fb6002a8 CB |
211 | } |
212 | ||
213 | return 0; | |
22731423 GU |
214 | |
215 | err_cleanup: | |
216 | timer_of_cleanup(to); | |
3a3e9f23 BD |
217 | err_reset: |
218 | reset_control_assert(rstc); | |
219 | reset_control_put(rstc); | |
22731423 GU |
220 | err_free: |
221 | kfree(to); | |
222 | return ret; | |
fb6002a8 CB |
223 | } |
224 | ||
17273395 | 225 | TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init); |
3a3e9f23 BD |
226 | |
227 | #ifdef CONFIG_ARCH_R9A07G044 | |
228 | static int __init ostm_probe(struct platform_device *pdev) | |
229 | { | |
230 | struct device *dev = &pdev->dev; | |
231 | ||
232 | return ostm_init(dev->of_node); | |
233 | } | |
234 | ||
235 | static const struct of_device_id ostm_of_table[] = { | |
236 | { .compatible = "renesas,ostm", }, | |
237 | { /* sentinel */ } | |
238 | }; | |
239 | ||
240 | static struct platform_driver ostm_device_driver = { | |
241 | .driver = { | |
242 | .name = "renesas_ostm", | |
243 | .of_match_table = of_match_ptr(ostm_of_table), | |
244 | .suppress_bind_attrs = true, | |
245 | }, | |
246 | }; | |
247 | builtin_platform_driver_probe(ostm_device_driver, ostm_probe); | |
248 | #endif |