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