Commit | Line | Data |
---|---|---|
c78275f3 | 1 | /* |
ceefc71d | 2 | * PTP 1588 clock for Freescale QorIQ 1588 timer |
c78275f3 RC |
3 | * |
4 | * Copyright (C) 2010 OMICRON electronics GmbH | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
375d6a1b JP |
20 | |
21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
22 | ||
c78275f3 RC |
23 | #include <linux/device.h> |
24 | #include <linux/hrtimer.h> | |
c78275f3 RC |
25 | #include <linux/interrupt.h> |
26 | #include <linux/kernel.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/of.h> | |
29 | #include <linux/of_platform.h> | |
30 | #include <linux/timex.h> | |
ceefc71d | 31 | #include <linux/slab.h> |
91305f28 | 32 | #include <linux/clk.h> |
c78275f3 | 33 | |
6c50c1ed | 34 | #include <linux/fsl/ptp_qoriq.h> |
ceefc71d | 35 | |
c78275f3 RC |
36 | /* |
37 | * Register access functions | |
38 | */ | |
39 | ||
ceefc71d YL |
40 | /* Caller must hold qoriq_ptp->lock. */ |
41 | static u64 tmr_cnt_read(struct qoriq_ptp *qoriq_ptp) | |
c78275f3 | 42 | { |
a8f62d0c | 43 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 RC |
44 | u64 ns; |
45 | u32 lo, hi; | |
46 | ||
a8f62d0c YL |
47 | lo = qoriq_read(®s->ctrl_regs->tmr_cnt_l); |
48 | hi = qoriq_read(®s->ctrl_regs->tmr_cnt_h); | |
c78275f3 RC |
49 | ns = ((u64) hi) << 32; |
50 | ns |= lo; | |
51 | return ns; | |
52 | } | |
53 | ||
ceefc71d YL |
54 | /* Caller must hold qoriq_ptp->lock. */ |
55 | static void tmr_cnt_write(struct qoriq_ptp *qoriq_ptp, u64 ns) | |
c78275f3 | 56 | { |
a8f62d0c | 57 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 RC |
58 | u32 hi = ns >> 32; |
59 | u32 lo = ns & 0xffffffff; | |
60 | ||
a8f62d0c YL |
61 | qoriq_write(®s->ctrl_regs->tmr_cnt_l, lo); |
62 | qoriq_write(®s->ctrl_regs->tmr_cnt_h, hi); | |
c78275f3 RC |
63 | } |
64 | ||
ceefc71d YL |
65 | /* Caller must hold qoriq_ptp->lock. */ |
66 | static void set_alarm(struct qoriq_ptp *qoriq_ptp) | |
c78275f3 | 67 | { |
a8f62d0c | 68 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 RC |
69 | u64 ns; |
70 | u32 lo, hi; | |
71 | ||
ceefc71d | 72 | ns = tmr_cnt_read(qoriq_ptp) + 1500000000ULL; |
c78275f3 | 73 | ns = div_u64(ns, 1000000000UL) * 1000000000ULL; |
ceefc71d | 74 | ns -= qoriq_ptp->tclk_period; |
c78275f3 RC |
75 | hi = ns >> 32; |
76 | lo = ns & 0xffffffff; | |
a8f62d0c YL |
77 | qoriq_write(®s->alarm_regs->tmr_alarm1_l, lo); |
78 | qoriq_write(®s->alarm_regs->tmr_alarm1_h, hi); | |
c78275f3 RC |
79 | } |
80 | ||
ceefc71d YL |
81 | /* Caller must hold qoriq_ptp->lock. */ |
82 | static void set_fipers(struct qoriq_ptp *qoriq_ptp) | |
c78275f3 | 83 | { |
a8f62d0c YL |
84 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
85 | ||
ceefc71d | 86 | set_alarm(qoriq_ptp); |
a8f62d0c YL |
87 | qoriq_write(®s->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1); |
88 | qoriq_write(®s->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2); | |
c78275f3 RC |
89 | } |
90 | ||
91 | /* | |
92 | * Interrupt service routine | |
93 | */ | |
94 | ||
95 | static irqreturn_t isr(int irq, void *priv) | |
96 | { | |
ceefc71d | 97 | struct qoriq_ptp *qoriq_ptp = priv; |
a8f62d0c | 98 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 RC |
99 | struct ptp_clock_event event; |
100 | u64 ns; | |
101 | u32 ack = 0, lo, hi, mask, val; | |
102 | ||
a8f62d0c | 103 | val = qoriq_read(®s->ctrl_regs->tmr_tevent); |
c78275f3 RC |
104 | |
105 | if (val & ETS1) { | |
106 | ack |= ETS1; | |
a8f62d0c YL |
107 | hi = qoriq_read(®s->etts_regs->tmr_etts1_h); |
108 | lo = qoriq_read(®s->etts_regs->tmr_etts1_l); | |
c78275f3 RC |
109 | event.type = PTP_CLOCK_EXTTS; |
110 | event.index = 0; | |
111 | event.timestamp = ((u64) hi) << 32; | |
112 | event.timestamp |= lo; | |
ceefc71d | 113 | ptp_clock_event(qoriq_ptp->clock, &event); |
c78275f3 RC |
114 | } |
115 | ||
116 | if (val & ETS2) { | |
117 | ack |= ETS2; | |
a8f62d0c YL |
118 | hi = qoriq_read(®s->etts_regs->tmr_etts2_h); |
119 | lo = qoriq_read(®s->etts_regs->tmr_etts2_l); | |
c78275f3 RC |
120 | event.type = PTP_CLOCK_EXTTS; |
121 | event.index = 1; | |
122 | event.timestamp = ((u64) hi) << 32; | |
123 | event.timestamp |= lo; | |
ceefc71d | 124 | ptp_clock_event(qoriq_ptp->clock, &event); |
c78275f3 RC |
125 | } |
126 | ||
127 | if (val & ALM2) { | |
128 | ack |= ALM2; | |
ceefc71d | 129 | if (qoriq_ptp->alarm_value) { |
c78275f3 RC |
130 | event.type = PTP_CLOCK_ALARM; |
131 | event.index = 0; | |
ceefc71d YL |
132 | event.timestamp = qoriq_ptp->alarm_value; |
133 | ptp_clock_event(qoriq_ptp->clock, &event); | |
c78275f3 | 134 | } |
ceefc71d YL |
135 | if (qoriq_ptp->alarm_interval) { |
136 | ns = qoriq_ptp->alarm_value + qoriq_ptp->alarm_interval; | |
c78275f3 RC |
137 | hi = ns >> 32; |
138 | lo = ns & 0xffffffff; | |
ceefc71d | 139 | spin_lock(&qoriq_ptp->lock); |
a8f62d0c YL |
140 | qoriq_write(®s->alarm_regs->tmr_alarm2_l, lo); |
141 | qoriq_write(®s->alarm_regs->tmr_alarm2_h, hi); | |
ceefc71d YL |
142 | spin_unlock(&qoriq_ptp->lock); |
143 | qoriq_ptp->alarm_value = ns; | |
c78275f3 | 144 | } else { |
a8f62d0c | 145 | qoriq_write(®s->ctrl_regs->tmr_tevent, ALM2); |
ceefc71d | 146 | spin_lock(&qoriq_ptp->lock); |
a8f62d0c | 147 | mask = qoriq_read(®s->ctrl_regs->tmr_temask); |
c78275f3 | 148 | mask &= ~ALM2EN; |
a8f62d0c | 149 | qoriq_write(®s->ctrl_regs->tmr_temask, mask); |
ceefc71d YL |
150 | spin_unlock(&qoriq_ptp->lock); |
151 | qoriq_ptp->alarm_value = 0; | |
152 | qoriq_ptp->alarm_interval = 0; | |
c78275f3 RC |
153 | } |
154 | } | |
155 | ||
156 | if (val & PP1) { | |
157 | ack |= PP1; | |
158 | event.type = PTP_CLOCK_PPS; | |
ceefc71d | 159 | ptp_clock_event(qoriq_ptp->clock, &event); |
c78275f3 RC |
160 | } |
161 | ||
162 | if (ack) { | |
a8f62d0c | 163 | qoriq_write(®s->ctrl_regs->tmr_tevent, ack); |
c78275f3 RC |
164 | return IRQ_HANDLED; |
165 | } else | |
166 | return IRQ_NONE; | |
167 | } | |
168 | ||
169 | /* | |
170 | * PTP clock operations | |
171 | */ | |
172 | ||
ceefc71d | 173 | static int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) |
c78275f3 | 174 | { |
42895116 UDB |
175 | u64 adj, diff; |
176 | u32 tmr_add; | |
c78275f3 | 177 | int neg_adj = 0; |
ceefc71d | 178 | struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); |
a8f62d0c | 179 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 | 180 | |
42895116 | 181 | if (scaled_ppm < 0) { |
c78275f3 | 182 | neg_adj = 1; |
42895116 | 183 | scaled_ppm = -scaled_ppm; |
c78275f3 | 184 | } |
ceefc71d | 185 | tmr_add = qoriq_ptp->tmr_add; |
c78275f3 | 186 | adj = tmr_add; |
42895116 UDB |
187 | |
188 | /* calculate diff as adj*(scaled_ppm/65536)/1000000 | |
189 | * and round() to the nearest integer | |
190 | */ | |
191 | adj *= scaled_ppm; | |
192 | diff = div_u64(adj, 8000000); | |
193 | diff = (diff >> 13) + ((diff >> 12) & 1); | |
c78275f3 RC |
194 | |
195 | tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; | |
196 | ||
a8f62d0c | 197 | qoriq_write(®s->ctrl_regs->tmr_add, tmr_add); |
c78275f3 RC |
198 | |
199 | return 0; | |
200 | } | |
201 | ||
ceefc71d | 202 | static int ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta) |
c78275f3 RC |
203 | { |
204 | s64 now; | |
205 | unsigned long flags; | |
ceefc71d | 206 | struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); |
c78275f3 | 207 | |
ceefc71d | 208 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
c78275f3 | 209 | |
ceefc71d | 210 | now = tmr_cnt_read(qoriq_ptp); |
c78275f3 | 211 | now += delta; |
ceefc71d YL |
212 | tmr_cnt_write(qoriq_ptp, now); |
213 | set_fipers(qoriq_ptp); | |
c78275f3 | 214 | |
ceefc71d | 215 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 | 216 | |
c78275f3 RC |
217 | return 0; |
218 | } | |
219 | ||
ceefc71d | 220 | static int ptp_qoriq_gettime(struct ptp_clock_info *ptp, |
d28fdf0f | 221 | struct timespec64 *ts) |
c78275f3 RC |
222 | { |
223 | u64 ns; | |
c78275f3 | 224 | unsigned long flags; |
ceefc71d | 225 | struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); |
c78275f3 | 226 | |
ceefc71d | 227 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
c78275f3 | 228 | |
ceefc71d | 229 | ns = tmr_cnt_read(qoriq_ptp); |
c78275f3 | 230 | |
ceefc71d | 231 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 | 232 | |
3359e7c2 RC |
233 | *ts = ns_to_timespec64(ns); |
234 | ||
c78275f3 RC |
235 | return 0; |
236 | } | |
237 | ||
ceefc71d | 238 | static int ptp_qoriq_settime(struct ptp_clock_info *ptp, |
d28fdf0f | 239 | const struct timespec64 *ts) |
c78275f3 RC |
240 | { |
241 | u64 ns; | |
242 | unsigned long flags; | |
ceefc71d | 243 | struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); |
c78275f3 | 244 | |
3359e7c2 | 245 | ns = timespec64_to_ns(ts); |
c78275f3 | 246 | |
ceefc71d | 247 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
c78275f3 | 248 | |
ceefc71d YL |
249 | tmr_cnt_write(qoriq_ptp, ns); |
250 | set_fipers(qoriq_ptp); | |
c78275f3 | 251 | |
ceefc71d | 252 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 RC |
253 | |
254 | return 0; | |
255 | } | |
256 | ||
ceefc71d | 257 | static int ptp_qoriq_enable(struct ptp_clock_info *ptp, |
c78275f3 RC |
258 | struct ptp_clock_request *rq, int on) |
259 | { | |
ceefc71d | 260 | struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); |
a8f62d0c | 261 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 RC |
262 | unsigned long flags; |
263 | u32 bit, mask; | |
264 | ||
265 | switch (rq->type) { | |
266 | case PTP_CLK_REQ_EXTTS: | |
267 | switch (rq->extts.index) { | |
268 | case 0: | |
269 | bit = ETS1EN; | |
270 | break; | |
271 | case 1: | |
272 | bit = ETS2EN; | |
273 | break; | |
274 | default: | |
275 | return -EINVAL; | |
276 | } | |
ceefc71d | 277 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
a8f62d0c | 278 | mask = qoriq_read(®s->ctrl_regs->tmr_temask); |
c78275f3 RC |
279 | if (on) |
280 | mask |= bit; | |
281 | else | |
282 | mask &= ~bit; | |
a8f62d0c | 283 | qoriq_write(®s->ctrl_regs->tmr_temask, mask); |
ceefc71d | 284 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 RC |
285 | return 0; |
286 | ||
287 | case PTP_CLK_REQ_PPS: | |
ceefc71d | 288 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
a8f62d0c | 289 | mask = qoriq_read(®s->ctrl_regs->tmr_temask); |
c78275f3 RC |
290 | if (on) |
291 | mask |= PP1EN; | |
292 | else | |
293 | mask &= ~PP1EN; | |
a8f62d0c | 294 | qoriq_write(®s->ctrl_regs->tmr_temask, mask); |
ceefc71d | 295 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 RC |
296 | return 0; |
297 | ||
298 | default: | |
299 | break; | |
300 | } | |
301 | ||
302 | return -EOPNOTSUPP; | |
303 | } | |
304 | ||
ceefc71d | 305 | static const struct ptp_clock_info ptp_qoriq_caps = { |
c78275f3 | 306 | .owner = THIS_MODULE, |
ceefc71d | 307 | .name = "qoriq ptp clock", |
c78275f3 | 308 | .max_adj = 512000, |
cd4baaaa | 309 | .n_alarm = 0, |
c78275f3 RC |
310 | .n_ext_ts = N_EXT_TS, |
311 | .n_per_out = 0, | |
4986b4f0 | 312 | .n_pins = 0, |
c78275f3 | 313 | .pps = 1, |
ceefc71d YL |
314 | .adjfine = ptp_qoriq_adjfine, |
315 | .adjtime = ptp_qoriq_adjtime, | |
316 | .gettime64 = ptp_qoriq_gettime, | |
317 | .settime64 = ptp_qoriq_settime, | |
318 | .enable = ptp_qoriq_enable, | |
c78275f3 RC |
319 | }; |
320 | ||
91305f28 YL |
321 | /** |
322 | * qoriq_ptp_nominal_freq - calculate nominal frequency according to | |
323 | * reference clock frequency | |
324 | * | |
325 | * @clk_src: reference clock frequency | |
326 | * | |
327 | * The nominal frequency is the desired clock frequency. | |
328 | * It should be less than the reference clock frequency. | |
329 | * It should be a factor of 1000MHz. | |
330 | * | |
331 | * Return the nominal frequency | |
332 | */ | |
333 | static u32 qoriq_ptp_nominal_freq(u32 clk_src) | |
334 | { | |
335 | u32 remainder = 0; | |
336 | ||
337 | clk_src /= 1000000; | |
338 | remainder = clk_src % 100; | |
339 | if (remainder) { | |
340 | clk_src -= remainder; | |
341 | clk_src += 100; | |
342 | } | |
343 | ||
344 | do { | |
345 | clk_src -= 100; | |
346 | ||
347 | } while (1000 % clk_src); | |
348 | ||
349 | return clk_src * 1000000; | |
350 | } | |
351 | ||
352 | /** | |
353 | * qoriq_ptp_auto_config - calculate a set of default configurations | |
354 | * | |
355 | * @qoriq_ptp: pointer to qoriq_ptp | |
356 | * @node: pointer to device_node | |
357 | * | |
358 | * If below dts properties are not provided, this function will be | |
359 | * called to calculate a set of default configurations for them. | |
360 | * "fsl,tclk-period" | |
361 | * "fsl,tmr-prsc" | |
362 | * "fsl,tmr-add" | |
363 | * "fsl,tmr-fiper1" | |
364 | * "fsl,tmr-fiper2" | |
365 | * "fsl,max-adj" | |
366 | * | |
367 | * Return 0 if success | |
368 | */ | |
369 | static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp, | |
370 | struct device_node *node) | |
371 | { | |
372 | struct clk *clk; | |
373 | u64 freq_comp; | |
374 | u64 max_adj; | |
375 | u32 nominal_freq; | |
74c05a33 | 376 | u32 remainder = 0; |
91305f28 YL |
377 | u32 clk_src = 0; |
378 | ||
379 | qoriq_ptp->cksel = DEFAULT_CKSEL; | |
380 | ||
381 | clk = of_clk_get(node, 0); | |
382 | if (!IS_ERR(clk)) { | |
383 | clk_src = clk_get_rate(clk); | |
384 | clk_put(clk); | |
385 | } | |
386 | ||
387 | if (clk_src <= 100000000UL) { | |
388 | pr_err("error reference clock value, or lower than 100MHz\n"); | |
389 | return -EINVAL; | |
390 | } | |
391 | ||
392 | nominal_freq = qoriq_ptp_nominal_freq(clk_src); | |
393 | if (!nominal_freq) | |
394 | return -EINVAL; | |
395 | ||
396 | qoriq_ptp->tclk_period = 1000000000UL / nominal_freq; | |
397 | qoriq_ptp->tmr_prsc = DEFAULT_TMR_PRSC; | |
398 | ||
399 | /* Calculate initial frequency compensation value for TMR_ADD register. | |
400 | * freq_comp = ceil(2^32 / freq_ratio) | |
401 | * freq_ratio = reference_clock_freq / nominal_freq | |
402 | */ | |
403 | freq_comp = ((u64)1 << 32) * nominal_freq; | |
74c05a33 YL |
404 | freq_comp = div_u64_rem(freq_comp, clk_src, &remainder); |
405 | if (remainder) | |
91305f28 YL |
406 | freq_comp++; |
407 | ||
408 | qoriq_ptp->tmr_add = freq_comp; | |
409 | qoriq_ptp->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - qoriq_ptp->tclk_period; | |
410 | qoriq_ptp->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - qoriq_ptp->tclk_period; | |
411 | ||
412 | /* max_adj = 1000000000 * (freq_ratio - 1.0) - 1 | |
413 | * freq_ratio = reference_clock_freq / nominal_freq | |
414 | */ | |
415 | max_adj = 1000000000ULL * (clk_src - nominal_freq); | |
74c05a33 | 416 | max_adj = div_u64(max_adj, nominal_freq) - 1; |
91305f28 YL |
417 | qoriq_ptp->caps.max_adj = max_adj; |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
ceefc71d | 422 | static int qoriq_ptp_probe(struct platform_device *dev) |
c78275f3 RC |
423 | { |
424 | struct device_node *node = dev->dev.of_node; | |
ceefc71d | 425 | struct qoriq_ptp *qoriq_ptp; |
a8f62d0c | 426 | struct qoriq_ptp_registers *regs; |
d28fdf0f | 427 | struct timespec64 now; |
c78275f3 RC |
428 | int err = -ENOMEM; |
429 | u32 tmr_ctrl; | |
430 | unsigned long flags; | |
a8f62d0c | 431 | void __iomem *base; |
c78275f3 | 432 | |
ceefc71d YL |
433 | qoriq_ptp = kzalloc(sizeof(*qoriq_ptp), GFP_KERNEL); |
434 | if (!qoriq_ptp) | |
c78275f3 RC |
435 | goto no_memory; |
436 | ||
91305f28 | 437 | err = -EINVAL; |
c78275f3 | 438 | |
ceefc71d | 439 | qoriq_ptp->caps = ptp_qoriq_caps; |
e58f6f4f | 440 | |
ceefc71d YL |
441 | if (of_property_read_u32(node, "fsl,cksel", &qoriq_ptp->cksel)) |
442 | qoriq_ptp->cksel = DEFAULT_CKSEL; | |
c78275f3 | 443 | |
c35ec779 | 444 | if (of_property_read_u32(node, |
ceefc71d | 445 | "fsl,tclk-period", &qoriq_ptp->tclk_period) || |
c35ec779 | 446 | of_property_read_u32(node, |
ceefc71d | 447 | "fsl,tmr-prsc", &qoriq_ptp->tmr_prsc) || |
c35ec779 | 448 | of_property_read_u32(node, |
ceefc71d | 449 | "fsl,tmr-add", &qoriq_ptp->tmr_add) || |
c35ec779 | 450 | of_property_read_u32(node, |
ceefc71d | 451 | "fsl,tmr-fiper1", &qoriq_ptp->tmr_fiper1) || |
c35ec779 | 452 | of_property_read_u32(node, |
ceefc71d | 453 | "fsl,tmr-fiper2", &qoriq_ptp->tmr_fiper2) || |
c35ec779 | 454 | of_property_read_u32(node, |
ceefc71d | 455 | "fsl,max-adj", &qoriq_ptp->caps.max_adj)) { |
91305f28 YL |
456 | pr_warn("device tree node missing required elements, try automatic configuration\n"); |
457 | ||
458 | if (qoriq_ptp_auto_config(qoriq_ptp, node)) | |
459 | goto no_config; | |
c78275f3 RC |
460 | } |
461 | ||
91305f28 YL |
462 | err = -ENODEV; |
463 | ||
ceefc71d | 464 | qoriq_ptp->irq = platform_get_irq(dev, 0); |
c78275f3 | 465 | |
ceefc71d | 466 | if (qoriq_ptp->irq < 0) { |
c78275f3 RC |
467 | pr_err("irq not in device tree\n"); |
468 | goto no_node; | |
469 | } | |
a8f62d0c | 470 | if (request_irq(qoriq_ptp->irq, isr, IRQF_SHARED, DRIVER, qoriq_ptp)) { |
c78275f3 RC |
471 | pr_err("request_irq failed\n"); |
472 | goto no_node; | |
473 | } | |
474 | ||
ceefc71d YL |
475 | qoriq_ptp->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0); |
476 | if (!qoriq_ptp->rsrc) { | |
c78275f3 RC |
477 | pr_err("no resource\n"); |
478 | goto no_resource; | |
479 | } | |
ceefc71d | 480 | if (request_resource(&iomem_resource, qoriq_ptp->rsrc)) { |
c78275f3 RC |
481 | pr_err("resource busy\n"); |
482 | goto no_resource; | |
483 | } | |
484 | ||
ceefc71d | 485 | spin_lock_init(&qoriq_ptp->lock); |
c78275f3 | 486 | |
a8f62d0c YL |
487 | base = ioremap(qoriq_ptp->rsrc->start, |
488 | resource_size(qoriq_ptp->rsrc)); | |
489 | if (!base) { | |
c78275f3 RC |
490 | pr_err("ioremap ptp registers failed\n"); |
491 | goto no_ioremap; | |
492 | } | |
a8f62d0c YL |
493 | |
494 | qoriq_ptp->base = base; | |
495 | ||
496 | if (of_device_is_compatible(node, "fsl,fman-ptp-timer")) { | |
497 | qoriq_ptp->regs.ctrl_regs = base + FMAN_CTRL_REGS_OFFSET; | |
498 | qoriq_ptp->regs.alarm_regs = base + FMAN_ALARM_REGS_OFFSET; | |
499 | qoriq_ptp->regs.fiper_regs = base + FMAN_FIPER_REGS_OFFSET; | |
500 | qoriq_ptp->regs.etts_regs = base + FMAN_ETTS_REGS_OFFSET; | |
501 | } else { | |
502 | qoriq_ptp->regs.ctrl_regs = base + CTRL_REGS_OFFSET; | |
503 | qoriq_ptp->regs.alarm_regs = base + ALARM_REGS_OFFSET; | |
504 | qoriq_ptp->regs.fiper_regs = base + FIPER_REGS_OFFSET; | |
505 | qoriq_ptp->regs.etts_regs = base + ETTS_REGS_OFFSET; | |
506 | } | |
507 | ||
f696a21c | 508 | ktime_get_real_ts64(&now); |
ceefc71d | 509 | ptp_qoriq_settime(&qoriq_ptp->caps, &now); |
c78275f3 RC |
510 | |
511 | tmr_ctrl = | |
ceefc71d YL |
512 | (qoriq_ptp->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT | |
513 | (qoriq_ptp->cksel & CKSEL_MASK) << CKSEL_SHIFT; | |
c78275f3 | 514 | |
ceefc71d | 515 | spin_lock_irqsave(&qoriq_ptp->lock, flags); |
c78275f3 | 516 | |
a8f62d0c YL |
517 | regs = &qoriq_ptp->regs; |
518 | qoriq_write(®s->ctrl_regs->tmr_ctrl, tmr_ctrl); | |
519 | qoriq_write(®s->ctrl_regs->tmr_add, qoriq_ptp->tmr_add); | |
520 | qoriq_write(®s->ctrl_regs->tmr_prsc, qoriq_ptp->tmr_prsc); | |
521 | qoriq_write(®s->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1); | |
522 | qoriq_write(®s->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2); | |
ceefc71d | 523 | set_alarm(qoriq_ptp); |
a8f62d0c | 524 | qoriq_write(®s->ctrl_regs->tmr_ctrl, tmr_ctrl|FIPERST|RTPE|TE|FRD); |
c78275f3 | 525 | |
ceefc71d | 526 | spin_unlock_irqrestore(&qoriq_ptp->lock, flags); |
c78275f3 | 527 | |
ceefc71d YL |
528 | qoriq_ptp->clock = ptp_clock_register(&qoriq_ptp->caps, &dev->dev); |
529 | if (IS_ERR(qoriq_ptp->clock)) { | |
530 | err = PTR_ERR(qoriq_ptp->clock); | |
c78275f3 RC |
531 | goto no_clock; |
532 | } | |
ceefc71d | 533 | qoriq_ptp->phc_index = ptp_clock_index(qoriq_ptp->clock); |
c78275f3 | 534 | |
ceefc71d | 535 | platform_set_drvdata(dev, qoriq_ptp); |
c78275f3 RC |
536 | |
537 | return 0; | |
538 | ||
539 | no_clock: | |
a8f62d0c | 540 | iounmap(qoriq_ptp->base); |
c78275f3 | 541 | no_ioremap: |
ceefc71d | 542 | release_resource(qoriq_ptp->rsrc); |
c78275f3 | 543 | no_resource: |
ceefc71d | 544 | free_irq(qoriq_ptp->irq, qoriq_ptp); |
91305f28 | 545 | no_config: |
c78275f3 | 546 | no_node: |
ceefc71d | 547 | kfree(qoriq_ptp); |
c78275f3 RC |
548 | no_memory: |
549 | return err; | |
550 | } | |
551 | ||
ceefc71d | 552 | static int qoriq_ptp_remove(struct platform_device *dev) |
c78275f3 | 553 | { |
ceefc71d | 554 | struct qoriq_ptp *qoriq_ptp = platform_get_drvdata(dev); |
a8f62d0c | 555 | struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; |
c78275f3 | 556 | |
a8f62d0c YL |
557 | qoriq_write(®s->ctrl_regs->tmr_temask, 0); |
558 | qoriq_write(®s->ctrl_regs->tmr_ctrl, 0); | |
c78275f3 | 559 | |
ceefc71d | 560 | ptp_clock_unregister(qoriq_ptp->clock); |
a8f62d0c | 561 | iounmap(qoriq_ptp->base); |
ceefc71d YL |
562 | release_resource(qoriq_ptp->rsrc); |
563 | free_irq(qoriq_ptp->irq, qoriq_ptp); | |
564 | kfree(qoriq_ptp); | |
c78275f3 RC |
565 | |
566 | return 0; | |
567 | } | |
568 | ||
94e5a2a8 | 569 | static const struct of_device_id match_table[] = { |
c78275f3 | 570 | { .compatible = "fsl,etsec-ptp" }, |
a8f62d0c | 571 | { .compatible = "fsl,fman-ptp-timer" }, |
c78275f3 RC |
572 | {}, |
573 | }; | |
23860063 | 574 | MODULE_DEVICE_TABLE(of, match_table); |
c78275f3 | 575 | |
ceefc71d | 576 | static struct platform_driver qoriq_ptp_driver = { |
c78275f3 | 577 | .driver = { |
ceefc71d | 578 | .name = "ptp_qoriq", |
c78275f3 | 579 | .of_match_table = match_table, |
c78275f3 | 580 | }, |
ceefc71d YL |
581 | .probe = qoriq_ptp_probe, |
582 | .remove = qoriq_ptp_remove, | |
c78275f3 RC |
583 | }; |
584 | ||
ceefc71d | 585 | module_platform_driver(qoriq_ptp_driver); |
c78275f3 | 586 | |
c2ec3ff6 | 587 | MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); |
ceefc71d | 588 | MODULE_DESCRIPTION("PTP clock for Freescale QorIQ 1588 timer"); |
c78275f3 | 589 | MODULE_LICENSE("GPL"); |