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> | |
12 | #include <linux/sched_clock.h> | |
13 | #include <linux/slab.h> | |
14 | ||
22731423 GU |
15 | #include "timer-of.h" |
16 | ||
fb6002a8 CB |
17 | /* |
18 | * The OSTM contains independent channels. | |
19 | * The first OSTM channel probed will be set up as a free running | |
20 | * clocksource. Additionally we will use this clocksource for the system | |
21 | * schedule timer sched_clock(). | |
22 | * | |
23 | * The second (or more) channel probed will be set up as an interrupt | |
24 | * driven clock event. | |
25 | */ | |
26 | ||
fb6002a8 CB |
27 | static void __iomem *system_clock; /* For sched_clock() */ |
28 | ||
29 | /* OSTM REGISTERS */ | |
30 | #define OSTM_CMP 0x000 /* RW,32 */ | |
31 | #define OSTM_CNT 0x004 /* R,32 */ | |
32 | #define OSTM_TE 0x010 /* R,8 */ | |
33 | #define OSTM_TS 0x014 /* W,8 */ | |
34 | #define OSTM_TT 0x018 /* W,8 */ | |
35 | #define OSTM_CTL 0x020 /* RW,8 */ | |
36 | ||
37 | #define TE 0x01 | |
38 | #define TS 0x01 | |
39 | #define TT 0x01 | |
40 | #define CTL_PERIODIC 0x00 | |
41 | #define CTL_ONESHOT 0x02 | |
42 | #define CTL_FREERUN 0x02 | |
43 | ||
22731423 | 44 | static void ostm_timer_stop(struct timer_of *to) |
fb6002a8 | 45 | { |
22731423 GU |
46 | if (readb(timer_of_base(to) + OSTM_TE) & TE) { |
47 | writeb(TT, timer_of_base(to) + OSTM_TT); | |
fb6002a8 CB |
48 | |
49 | /* | |
50 | * Read back the register simply to confirm the write operation | |
51 | * has completed since I/O writes can sometimes get queued by | |
52 | * the bus architecture. | |
53 | */ | |
22731423 | 54 | while (readb(timer_of_base(to) + OSTM_TE) & TE) |
fb6002a8 CB |
55 | ; |
56 | } | |
57 | } | |
58 | ||
22731423 | 59 | static int __init ostm_init_clksrc(struct timer_of *to) |
fb6002a8 | 60 | { |
22731423 | 61 | ostm_timer_stop(to); |
fb6002a8 | 62 | |
22731423 GU |
63 | writel(0, timer_of_base(to) + OSTM_CMP); |
64 | writeb(CTL_FREERUN, timer_of_base(to) + OSTM_CTL); | |
65 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 | 66 | |
b35a5e59 GU |
67 | return clocksource_mmio_init(timer_of_base(to) + OSTM_CNT, |
68 | to->np->full_name, timer_of_rate(to), 300, | |
69 | 32, clocksource_mmio_readl_up); | |
fb6002a8 CB |
70 | } |
71 | ||
72 | static u64 notrace ostm_read_sched_clock(void) | |
73 | { | |
74 | return readl(system_clock); | |
75 | } | |
76 | ||
22731423 | 77 | static void __init ostm_init_sched_clock(struct timer_of *to) |
fb6002a8 | 78 | { |
22731423 GU |
79 | system_clock = timer_of_base(to) + OSTM_CNT; |
80 | sched_clock_register(ostm_read_sched_clock, 32, timer_of_rate(to)); | |
fb6002a8 CB |
81 | } |
82 | ||
83 | static int ostm_clock_event_next(unsigned long delta, | |
22731423 | 84 | struct clock_event_device *ced) |
fb6002a8 | 85 | { |
22731423 | 86 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 87 | |
22731423 | 88 | ostm_timer_stop(to); |
fb6002a8 | 89 | |
22731423 GU |
90 | writel(delta, timer_of_base(to) + OSTM_CMP); |
91 | writeb(CTL_ONESHOT, timer_of_base(to) + OSTM_CTL); | |
92 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 CB |
93 | |
94 | return 0; | |
95 | } | |
96 | ||
97 | static int ostm_shutdown(struct clock_event_device *ced) | |
98 | { | |
22731423 | 99 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 100 | |
22731423 | 101 | ostm_timer_stop(to); |
fb6002a8 CB |
102 | |
103 | return 0; | |
104 | } | |
105 | static int ostm_set_periodic(struct clock_event_device *ced) | |
106 | { | |
22731423 | 107 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 CB |
108 | |
109 | if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced)) | |
22731423 | 110 | ostm_timer_stop(to); |
fb6002a8 | 111 | |
22731423 GU |
112 | writel(timer_of_period(to) - 1, timer_of_base(to) + OSTM_CMP); |
113 | writeb(CTL_PERIODIC, timer_of_base(to) + OSTM_CTL); | |
114 | writeb(TS, timer_of_base(to) + OSTM_TS); | |
fb6002a8 CB |
115 | |
116 | return 0; | |
117 | } | |
118 | ||
119 | static int ostm_set_oneshot(struct clock_event_device *ced) | |
120 | { | |
22731423 | 121 | struct timer_of *to = to_timer_of(ced); |
fb6002a8 | 122 | |
22731423 | 123 | ostm_timer_stop(to); |
fb6002a8 CB |
124 | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id) | |
129 | { | |
22731423 | 130 | struct clock_event_device *ced = dev_id; |
fb6002a8 | 131 | |
22731423 GU |
132 | if (clockevent_state_oneshot(ced)) |
133 | ostm_timer_stop(to_timer_of(ced)); | |
fb6002a8 CB |
134 | |
135 | /* notify clockevent layer */ | |
22731423 GU |
136 | if (ced->event_handler) |
137 | ced->event_handler(ced); | |
fb6002a8 CB |
138 | |
139 | return IRQ_HANDLED; | |
140 | } | |
141 | ||
22731423 | 142 | static int __init ostm_init_clkevt(struct timer_of *to) |
fb6002a8 | 143 | { |
22731423 | 144 | struct clock_event_device *ced = &to->clkevt; |
fb6002a8 | 145 | |
fb6002a8 CB |
146 | ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC; |
147 | ced->set_state_shutdown = ostm_shutdown; | |
148 | ced->set_state_periodic = ostm_set_periodic; | |
149 | ced->set_state_oneshot = ostm_set_oneshot; | |
150 | ced->set_next_event = ostm_clock_event_next; | |
151 | ced->shift = 32; | |
152 | ced->rating = 300; | |
153 | ced->cpumask = cpumask_of(0); | |
22731423 GU |
154 | clockevents_config_and_register(ced, timer_of_rate(to), 0xf, |
155 | 0xffffffff); | |
fb6002a8 CB |
156 | |
157 | return 0; | |
158 | } | |
159 | ||
160 | static int __init ostm_init(struct device_node *np) | |
161 | { | |
22731423 GU |
162 | struct timer_of *to; |
163 | int ret; | |
fb6002a8 | 164 | |
22731423 GU |
165 | to = kzalloc(sizeof(*to), GFP_KERNEL); |
166 | if (!to) | |
167 | return -ENOMEM; | |
fb6002a8 | 168 | |
22731423 GU |
169 | to->flags = TIMER_OF_BASE | TIMER_OF_CLOCK; |
170 | if (system_clock) { | |
171 | /* | |
172 | * clock sources don't use interrupts, clock events do | |
173 | */ | |
174 | to->flags |= TIMER_OF_IRQ; | |
175 | to->of_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; | |
176 | to->of_irq.handler = ostm_timer_interrupt; | |
fb6002a8 CB |
177 | } |
178 | ||
22731423 GU |
179 | ret = timer_of_init(np, to); |
180 | if (ret) | |
181 | goto err_free; | |
fb6002a8 CB |
182 | |
183 | /* | |
184 | * First probed device will be used as system clocksource. Any | |
185 | * additional devices will be used as clock events. | |
186 | */ | |
187 | if (!system_clock) { | |
22731423 GU |
188 | ret = ostm_init_clksrc(to); |
189 | if (ret) | |
190 | goto err_cleanup; | |
fb6002a8 | 191 | |
22731423 | 192 | ostm_init_sched_clock(to); |
b35a5e59 | 193 | pr_info("%pOF: used for clocksource\n", np); |
fb6002a8 | 194 | } else { |
22731423 GU |
195 | ret = ostm_init_clkevt(to); |
196 | if (ret) | |
197 | goto err_cleanup; | |
fb6002a8 | 198 | |
b35a5e59 | 199 | pr_info("%pOF: used for clock events\n", np); |
fb6002a8 CB |
200 | } |
201 | ||
202 | return 0; | |
22731423 GU |
203 | |
204 | err_cleanup: | |
205 | timer_of_cleanup(to); | |
206 | err_free: | |
207 | kfree(to); | |
208 | return ret; | |
fb6002a8 CB |
209 | } |
210 | ||
17273395 | 211 | TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init); |