blob: 663255774c0b08adedac00d8517f1de6a1ab51ab [file] [log] [blame]
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * PTP hardware clock driver for the IDT ClockMatrix(TM) family of timing and
4 * synchronization devices.
5 *
6 * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
7 */
8#include <linux/firmware.h>
9#include <linux/i2c.h>
10#include <linux/module.h>
11#include <linux/ptp_clock_kernel.h>
12#include <linux/delay.h>
Vincent Cheng425d2b12020-05-01 23:35:38 -040013#include <linux/jiffies.h>
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -040014#include <linux/kernel.h>
15#include <linux/timekeeping.h>
Min Li7ea5fda2020-07-28 16:00:30 -040016#include <linux/string.h>
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -040017
18#include "ptp_private.h"
19#include "ptp_clockmatrix.h"
20
21MODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family");
22MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
23MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
24MODULE_VERSION("1.0");
25MODULE_LICENSE("GPL");
26
Min Li7ea5fda2020-07-28 16:00:30 -040027/*
28 * The name of the firmware file to be loaded
29 * over-rides any automatic selection
30 */
31static char *firmware;
32module_param(firmware, charp, 0);
33
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -040034#define SETTIME_CORRECTION (0)
35
Vincent Cheng425d2b12020-05-01 23:35:38 -040036static long set_write_phase_ready(struct ptp_clock_info *ptp)
37{
38 struct idtcm_channel *channel =
39 container_of(ptp, struct idtcm_channel, caps);
40
41 channel->write_phase_ready = 1;
42
43 return 0;
44}
45
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -040046static int char_array_to_timespec(u8 *buf,
47 u8 count,
48 struct timespec64 *ts)
49{
50 u8 i;
51 u64 nsec;
52 time64_t sec;
53
54 if (count < TOD_BYTE_COUNT)
55 return 1;
56
57 /* Sub-nanoseconds are in buf[0]. */
58 nsec = buf[4];
59 for (i = 0; i < 3; i++) {
60 nsec <<= 8;
61 nsec |= buf[3 - i];
62 }
63
64 sec = buf[10];
65 for (i = 0; i < 5; i++) {
66 sec <<= 8;
67 sec |= buf[9 - i];
68 }
69
70 ts->tv_sec = sec;
71 ts->tv_nsec = nsec;
72
73 return 0;
74}
75
76static int timespec_to_char_array(struct timespec64 const *ts,
77 u8 *buf,
78 u8 count)
79{
80 u8 i;
81 s32 nsec;
82 time64_t sec;
83
84 if (count < TOD_BYTE_COUNT)
85 return 1;
86
87 nsec = ts->tv_nsec;
88 sec = ts->tv_sec;
89
90 /* Sub-nanoseconds are in buf[0]. */
91 buf[0] = 0;
92 for (i = 1; i < 5; i++) {
93 buf[i] = nsec & 0xff;
94 nsec >>= 8;
95 }
96
97 for (i = 5; i < TOD_BYTE_COUNT; i++) {
98
99 buf[i] = sec & 0xff;
100 sec >>= 8;
101 }
102
103 return 0;
104}
105
Min Li3cb2e6d2020-11-24 21:58:35 -0500106static int idtcm_strverscmp(const char *version1, const char *version2)
Min Li7ea5fda2020-07-28 16:00:30 -0400107{
Min Li3cb2e6d2020-11-24 21:58:35 -0500108 u8 ver1[3], ver2[3];
109 int i;
Min Li7ea5fda2020-07-28 16:00:30 -0400110
Min Li3cb2e6d2020-11-24 21:58:35 -0500111 if (sscanf(version1, "%hhu.%hhu.%hhu",
112 &ver1[0], &ver1[1], &ver1[2]) != 3)
113 return -1;
114 if (sscanf(version2, "%hhu.%hhu.%hhu",
115 &ver2[0], &ver2[1], &ver2[2]) != 3)
116 return -1;
117
118 for (i = 0; i < 3; i++) {
119 if (ver1[i] > ver2[i])
120 return 1;
121 if (ver1[i] < ver2[i])
Min Li7ea5fda2020-07-28 16:00:30 -0400122 return -1;
Min Li7ea5fda2020-07-28 16:00:30 -0400123 }
Min Li3cb2e6d2020-11-24 21:58:35 -0500124
125 return 0;
Min Li7ea5fda2020-07-28 16:00:30 -0400126}
127
Min Li957ff422020-08-18 10:41:22 -0400128static int idtcm_xfer_read(struct idtcm *idtcm,
129 u8 regaddr,
130 u8 *buf,
131 u16 count)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400132{
133 struct i2c_client *client = idtcm->client;
134 struct i2c_msg msg[2];
135 int cnt;
Min Li957ff422020-08-18 10:41:22 -0400136 char *fmt = "i2c_transfer failed at %d in %s, at addr: %04X!\n";
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400137
138 msg[0].addr = client->addr;
139 msg[0].flags = 0;
140 msg[0].len = 1;
141 msg[0].buf = &regaddr;
142
143 msg[1].addr = client->addr;
Min Li957ff422020-08-18 10:41:22 -0400144 msg[1].flags = I2C_M_RD;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400145 msg[1].len = count;
146 msg[1].buf = buf;
147
148 cnt = i2c_transfer(client->adapter, msg, 2);
149
150 if (cnt < 0) {
Min Li7ea5fda2020-07-28 16:00:30 -0400151 dev_err(&client->dev,
152 fmt,
153 __LINE__,
154 __func__,
Min Li7ea5fda2020-07-28 16:00:30 -0400155 regaddr);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400156 return cnt;
157 } else if (cnt != 2) {
158 dev_err(&client->dev,
159 "i2c_transfer sent only %d of %d messages\n", cnt, 2);
160 return -EIO;
161 }
162
163 return 0;
164}
165
Min Li957ff422020-08-18 10:41:22 -0400166static int idtcm_xfer_write(struct idtcm *idtcm,
167 u8 regaddr,
168 u8 *buf,
169 u16 count)
170{
171 struct i2c_client *client = idtcm->client;
172 /* we add 1 byte for device register */
173 u8 msg[IDTCM_MAX_WRITE_COUNT + 1];
174 int cnt;
175 char *fmt = "i2c_master_send failed at %d in %s, at addr: %04X!\n";
176
177 if (count > IDTCM_MAX_WRITE_COUNT)
178 return -EINVAL;
179
180 msg[0] = regaddr;
181 memcpy(&msg[1], buf, count);
182
183 cnt = i2c_master_send(client, msg, count + 1);
184
185 if (cnt < 0) {
186 dev_err(&client->dev,
187 fmt,
188 __LINE__,
189 __func__,
190 regaddr);
191 return cnt;
192 }
193
194 return 0;
195}
196
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400197static int idtcm_page_offset(struct idtcm *idtcm, u8 val)
198{
199 u8 buf[4];
200 int err;
201
202 if (idtcm->page_offset == val)
203 return 0;
204
205 buf[0] = 0x0;
206 buf[1] = val;
207 buf[2] = 0x10;
208 buf[3] = 0x20;
209
Min Li957ff422020-08-18 10:41:22 -0400210 err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400211
Min Li7ea5fda2020-07-28 16:00:30 -0400212 if (err) {
213 idtcm->page_offset = 0xff;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400214 dev_err(&idtcm->client->dev, "failed to set page offset\n");
Min Li7ea5fda2020-07-28 16:00:30 -0400215 } else {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400216 idtcm->page_offset = val;
Min Li7ea5fda2020-07-28 16:00:30 -0400217 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400218
219 return err;
220}
221
222static int _idtcm_rdwr(struct idtcm *idtcm,
223 u16 regaddr,
224 u8 *buf,
225 u16 count,
226 bool write)
227{
228 u8 hi;
229 u8 lo;
230 int err;
231
232 hi = (regaddr >> 8) & 0xff;
233 lo = regaddr & 0xff;
234
235 err = idtcm_page_offset(idtcm, hi);
236
237 if (err)
Min Li957ff422020-08-18 10:41:22 -0400238 return err;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400239
Min Li957ff422020-08-18 10:41:22 -0400240 if (write)
241 return idtcm_xfer_write(idtcm, lo, buf, count);
242
243 return idtcm_xfer_read(idtcm, lo, buf, count);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400244}
245
246static int idtcm_read(struct idtcm *idtcm,
247 u16 module,
248 u16 regaddr,
249 u8 *buf,
250 u16 count)
251{
252 return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false);
253}
254
255static int idtcm_write(struct idtcm *idtcm,
256 u16 module,
257 u16 regaddr,
258 u8 *buf,
259 u16 count)
260{
261 return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
262}
263
264static int _idtcm_gettime(struct idtcm_channel *channel,
265 struct timespec64 *ts)
266{
267 struct idtcm *idtcm = channel->idtcm;
268 u8 buf[TOD_BYTE_COUNT];
Min Li7ea5fda2020-07-28 16:00:30 -0400269 u8 timeout = 10;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400270 u8 trigger;
271 int err;
272
273 err = idtcm_read(idtcm, channel->tod_read_primary,
274 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
275 if (err)
276 return err;
277
278 trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
279 trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
Min Li7ea5fda2020-07-28 16:00:30 -0400280 trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400281
282 err = idtcm_write(idtcm, channel->tod_read_primary,
283 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400284 if (err)
285 return err;
286
Min Li7ea5fda2020-07-28 16:00:30 -0400287 /* wait trigger to be 0 */
288 while (trigger & TOD_READ_TRIGGER_MASK) {
289
290 if (idtcm->calculate_overhead_flag)
291 idtcm->start_time = ktime_get_raw();
292
293 err = idtcm_read(idtcm, channel->tod_read_primary,
294 TOD_READ_PRIMARY_CMD, &trigger,
295 sizeof(trigger));
296
297 if (err)
298 return err;
299
300 if (--timeout == 0)
301 return -EIO;
302 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400303
304 err = idtcm_read(idtcm, channel->tod_read_primary,
305 TOD_READ_PRIMARY, buf, sizeof(buf));
306
307 if (err)
308 return err;
309
310 err = char_array_to_timespec(buf, sizeof(buf), ts);
311
312 return err;
313}
314
315static int _sync_pll_output(struct idtcm *idtcm,
316 u8 pll,
317 u8 sync_src,
318 u8 qn,
319 u8 qn_plus_1)
320{
321 int err;
322 u8 val;
323 u16 sync_ctrl0;
324 u16 sync_ctrl1;
Min Li7ea5fda2020-07-28 16:00:30 -0400325 u8 temp;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400326
327 if ((qn == 0) && (qn_plus_1 == 0))
328 return 0;
329
330 switch (pll) {
331 case 0:
332 sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
333 sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
334 break;
335 case 1:
336 sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
337 sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
338 break;
339 case 2:
340 sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
341 sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
342 break;
343 case 3:
344 sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
345 sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
346 break;
347 case 4:
348 sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
349 sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
350 break;
351 case 5:
352 sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
353 sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
354 break;
355 case 6:
356 sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
357 sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
358 break;
359 case 7:
360 sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
361 sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
362 break;
363 default:
364 return -EINVAL;
365 }
366
367 val = SYNCTRL1_MASTER_SYNC_RST;
368
369 /* Place master sync in reset */
370 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
371 if (err)
372 return err;
373
374 err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
375 if (err)
376 return err;
377
378 /* Set sync trigger mask */
379 val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
380
381 if (qn)
382 val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
383
384 if (qn_plus_1)
385 val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
386
387 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
388 if (err)
389 return err;
390
Min Li7ea5fda2020-07-28 16:00:30 -0400391 /* PLL5 can have OUT8 as second additional output. */
392 if ((pll == 5) && (qn_plus_1 != 0)) {
393 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
394 &temp, sizeof(temp));
395 if (err)
396 return err;
397
398 temp &= ~(Q9_TO_Q8_SYNC_TRIG);
399
400 err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
401 &temp, sizeof(temp));
402 if (err)
403 return err;
404
405 temp |= Q9_TO_Q8_SYNC_TRIG;
406
407 err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
408 &temp, sizeof(temp));
409 if (err)
410 return err;
411 }
412
413 /* PLL6 can have OUT11 as second additional output. */
414 if ((pll == 6) && (qn_plus_1 != 0)) {
415 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
416 &temp, sizeof(temp));
417 if (err)
418 return err;
419
420 temp &= ~(Q10_TO_Q11_SYNC_TRIG);
421
422 err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
423 &temp, sizeof(temp));
424 if (err)
425 return err;
426
427 temp |= Q10_TO_Q11_SYNC_TRIG;
428
429 err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
430 &temp, sizeof(temp));
431 if (err)
432 return err;
433 }
434
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400435 /* Place master sync out of reset */
436 val &= ~(SYNCTRL1_MASTER_SYNC_RST);
437 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
438
439 return err;
440}
441
Min Li7ea5fda2020-07-28 16:00:30 -0400442static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
443{
444 int err = 0;
445
446 switch (tod_addr) {
447 case TOD_0:
448 *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
449 break;
450 case TOD_1:
451 *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
452 break;
453 case TOD_2:
454 *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
455 break;
456 case TOD_3:
457 *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
458 break;
459 default:
460 err = -EINVAL;
461 }
462
463 return err;
464}
465
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400466static int idtcm_sync_pps_output(struct idtcm_channel *channel)
467{
468 struct idtcm *idtcm = channel->idtcm;
469
470 u8 pll;
471 u8 sync_src;
472 u8 qn;
473 u8 qn_plus_1;
474 int err = 0;
Min Li7ea5fda2020-07-28 16:00:30 -0400475 u8 out8_mux = 0;
476 u8 out11_mux = 0;
477 u8 temp;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400478
479 u16 output_mask = channel->output_mask;
480
Min Li7ea5fda2020-07-28 16:00:30 -0400481 err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
482 if (err)
483 return err;
484
485 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
486 &temp, sizeof(temp));
487 if (err)
488 return err;
489
490 if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
491 Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
492 out8_mux = 1;
493
494 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
495 &temp, sizeof(temp));
496 if (err)
497 return err;
498
499 if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
500 Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
501 out11_mux = 1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400502
503 for (pll = 0; pll < 8; pll++) {
Min Li7ea5fda2020-07-28 16:00:30 -0400504 qn = 0;
505 qn_plus_1 = 0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400506
507 if (pll < 4) {
508 /* First 4 pll has 2 outputs */
Min Li7ea5fda2020-07-28 16:00:30 -0400509 qn = output_mask & 0x1;
510 output_mask = output_mask >> 1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400511 qn_plus_1 = output_mask & 0x1;
512 output_mask = output_mask >> 1;
Min Li7ea5fda2020-07-28 16:00:30 -0400513 } else if (pll == 4) {
514 if (out8_mux == 0) {
515 qn = output_mask & 0x1;
516 output_mask = output_mask >> 1;
517 }
518 } else if (pll == 5) {
519 if (out8_mux) {
520 qn_plus_1 = output_mask & 0x1;
521 output_mask = output_mask >> 1;
522 }
523 qn = output_mask & 0x1;
524 output_mask = output_mask >> 1;
525 } else if (pll == 6) {
526 qn = output_mask & 0x1;
527 output_mask = output_mask >> 1;
528 if (out11_mux) {
529 qn_plus_1 = output_mask & 0x1;
530 output_mask = output_mask >> 1;
531 }
532 } else if (pll == 7) {
533 if (out11_mux == 0) {
534 qn = output_mask & 0x1;
535 output_mask = output_mask >> 1;
536 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400537 }
538
539 if ((qn != 0) || (qn_plus_1 != 0))
540 err = _sync_pll_output(idtcm, pll, sync_src, qn,
541 qn_plus_1);
542
543 if (err)
544 return err;
545 }
546
547 return err;
548}
549
Min Li7ea5fda2020-07-28 16:00:30 -0400550static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400551 struct timespec64 const *ts,
552 enum hw_tod_write_trig_sel wr_trig)
553{
554 struct idtcm *idtcm = channel->idtcm;
555
556 u8 buf[TOD_BYTE_COUNT];
557 u8 cmd;
558 int err;
559 struct timespec64 local_ts = *ts;
560 s64 total_overhead_ns;
561
562 /* Configure HW TOD write trigger. */
563 err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
564 &cmd, sizeof(cmd));
565
566 if (err)
567 return err;
568
569 cmd &= ~(0x0f);
570 cmd |= wr_trig | 0x08;
571
572 err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
573 &cmd, sizeof(cmd));
574
575 if (err)
576 return err;
577
578 if (wr_trig != HW_TOD_WR_TRIG_SEL_MSB) {
579
580 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
581
582 if (err)
583 return err;
584
585 err = idtcm_write(idtcm, channel->hw_dpll_n,
586 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
587
588 if (err)
589 return err;
590 }
591
592 /* ARM HW TOD write trigger. */
593 cmd &= ~(0x08);
594
595 err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
596 &cmd, sizeof(cmd));
597
598 if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
599
600 if (idtcm->calculate_overhead_flag) {
Vincent Cheng1ece2fb2020-01-07 09:47:57 -0500601 /* Assumption: I2C @ 400KHz */
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400602 total_overhead_ns = ktime_to_ns(ktime_get_raw()
603 - idtcm->start_time)
604 + idtcm->tod_write_overhead_ns
605 + SETTIME_CORRECTION;
606
607 timespec64_add_ns(&local_ts, total_overhead_ns);
608
609 idtcm->calculate_overhead_flag = 0;
610 }
611
612 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
613
614 if (err)
615 return err;
616
617 err = idtcm_write(idtcm, channel->hw_dpll_n,
618 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
619 }
620
621 return err;
622}
623
Min Li7ea5fda2020-07-28 16:00:30 -0400624static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
625 struct timespec64 const *ts,
626 enum scsr_tod_write_trig_sel wr_trig,
627 enum scsr_tod_write_type_sel wr_type)
628{
629 struct idtcm *idtcm = channel->idtcm;
630 unsigned char buf[TOD_BYTE_COUNT], cmd;
631 struct timespec64 local_ts = *ts;
632 int err, count = 0;
633
634 timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
635
636 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
637
638 if (err)
639 return err;
640
641 err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
642 buf, sizeof(buf));
643 if (err)
644 return err;
645
646 /* Trigger the write operation. */
647 err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
648 &cmd, sizeof(cmd));
649 if (err)
650 return err;
651
652 cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
653 cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
654 cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
655 cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
656
657 err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
658 &cmd, sizeof(cmd));
659 if (err)
660 return err;
661
662 /* Wait for the operation to complete. */
663 while (1) {
664 /* pps trigger takes up to 1 sec to complete */
665 if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
666 msleep(50);
667
668 err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
669 &cmd, sizeof(cmd));
670 if (err)
671 return err;
672
673 if (cmd == 0)
674 break;
675
676 if (++count > 20) {
677 dev_err(&idtcm->client->dev,
678 "Timed out waiting for the write counter\n");
679 return -EIO;
680 }
681 }
682
683 return 0;
684}
685
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400686static int _idtcm_settime(struct idtcm_channel *channel,
687 struct timespec64 const *ts,
688 enum hw_tod_write_trig_sel wr_trig)
689{
690 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400691 int err;
692 int i;
693 u8 trig_sel;
694
Min Li7ea5fda2020-07-28 16:00:30 -0400695 err = _idtcm_set_dpll_hw_tod(channel, ts, wr_trig);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400696
697 if (err)
698 return err;
699
700 /* Wait for the operation to complete. */
701 for (i = 0; i < 10000; i++) {
702 err = idtcm_read(idtcm, channel->hw_dpll_n,
703 HW_DPLL_TOD_CTRL_1, &trig_sel,
704 sizeof(trig_sel));
705
706 if (err)
707 return err;
708
709 if (trig_sel == 0x4a)
710 break;
711
712 err = 1;
713 }
714
Min Li7ea5fda2020-07-28 16:00:30 -0400715 if (err) {
716 dev_err(&idtcm->client->dev,
717 "Failed at line %d in func %s!\n",
718 __LINE__,
719 __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400720 return err;
Min Li7ea5fda2020-07-28 16:00:30 -0400721 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400722
Min Li7ea5fda2020-07-28 16:00:30 -0400723 return idtcm_sync_pps_output(channel);
724}
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400725
Min Li7ea5fda2020-07-28 16:00:30 -0400726static int _idtcm_settime_v487(struct idtcm_channel *channel,
727 struct timespec64 const *ts,
728 enum scsr_tod_write_type_sel wr_type)
729{
730 return _idtcm_set_dpll_scsr_tod(channel, ts,
731 SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
732 wr_type);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400733}
734
735static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
736 s32 offset_ns)
737{
738 int err;
739 int i;
740 struct idtcm *idtcm = channel->idtcm;
741
742 u8 buf[4];
743
744 for (i = 0; i < 4; i++) {
745 buf[i] = 0xff & (offset_ns);
746 offset_ns >>= 8;
747 }
748
749 err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
750 buf, sizeof(buf));
751
752 return err;
753}
754
755static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
756 u32 max_ffo_ppb)
757{
758 int err;
759 u8 i;
760 struct idtcm *idtcm = channel->idtcm;
761
762 u8 buf[3];
763
764 if (max_ffo_ppb & 0xff000000)
765 max_ffo_ppb = 0;
766
767 for (i = 0; i < 3; i++) {
768 buf[i] = 0xff & (max_ffo_ppb);
769 max_ffo_ppb >>= 8;
770 }
771
772 err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
773 PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
774
775 return err;
776}
777
778static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
779{
780 int err;
781 struct idtcm *idtcm = channel->idtcm;
782
783 u8 buf;
784
785 err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
786 &buf, sizeof(buf));
787
788 if (err)
789 return err;
790
791 if (buf == 0) {
792 buf = 0x01;
793 err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
794 PULL_IN_CTRL, &buf, sizeof(buf));
795 } else {
796 err = -EBUSY;
797 }
798
799 return err;
800}
801
802static int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
803 s32 offset_ns,
804 u32 max_ffo_ppb)
805{
806 int err;
807
808 err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
809
810 if (err)
811 return err;
812
813 err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
814
815 if (err)
816 return err;
817
818 err = idtcm_start_phase_pull_in(channel);
819
820 return err;
821}
822
Min Li7ea5fda2020-07-28 16:00:30 -0400823static int set_tod_write_overhead(struct idtcm_channel *channel)
824{
825 struct idtcm *idtcm = channel->idtcm;
826 s64 current_ns = 0;
827 s64 lowest_ns = 0;
828 int err;
829 u8 i;
830
831 ktime_t start;
832 ktime_t stop;
833
834 char buf[TOD_BYTE_COUNT] = {0};
835
836 /* Set page offset */
837 idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
838 buf, sizeof(buf));
839
840 for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
841
842 start = ktime_get_raw();
843
844 err = idtcm_write(idtcm, channel->hw_dpll_n,
845 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
846
847 if (err)
848 return err;
849
850 stop = ktime_get_raw();
851
852 current_ns = ktime_to_ns(stop - start);
853
854 if (i == 0) {
855 lowest_ns = current_ns;
856 } else {
857 if (current_ns < lowest_ns)
858 lowest_ns = current_ns;
859 }
860 }
861
862 idtcm->tod_write_overhead_ns = lowest_ns;
863
864 return err;
865}
866
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400867static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
868{
869 int err;
870 struct idtcm *idtcm = channel->idtcm;
871 struct timespec64 ts;
872 s64 now;
873
874 if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
875 err = idtcm_do_phase_pull_in(channel, delta, 0);
876 } else {
877 idtcm->calculate_overhead_flag = 1;
878
Min Li7ea5fda2020-07-28 16:00:30 -0400879 err = set_tod_write_overhead(channel);
880
881 if (err)
882 return err;
883
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400884 err = _idtcm_gettime(channel, &ts);
885
886 if (err)
887 return err;
888
889 now = timespec64_to_ns(&ts);
890 now += delta;
891
892 ts = ns_to_timespec64(now);
893
894 err = _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
895 }
896
897 return err;
898}
899
900static int idtcm_state_machine_reset(struct idtcm *idtcm)
901{
902 int err;
903 u8 byte = SM_RESET_CMD;
904
905 err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
906
907 if (!err)
908 msleep_interruptible(POST_SM_RESET_DELAY_MS);
909
910 return err;
911}
912
913static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
914{
Vincent Cheng1ece2fb2020-01-07 09:47:57 -0500915 return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400916}
917
918static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
919{
920 int err;
921 u8 buf[2] = {0};
922
923 err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
924
925 *product_id = (buf[1] << 8) | buf[0];
926
927 return err;
928}
929
930static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
931{
932 int err;
933 u8 buf = 0;
934
935 err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
936
937 *major = buf >> 1;
938
939 return err;
940}
941
942static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
943{
944 return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
945}
946
947static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
948{
949 return idtcm_read(idtcm,
950 GENERAL_STATUS,
951 HOTFIX_REL,
952 hotfix,
953 sizeof(u8));
954}
955
Vincent Cheng1ece2fb2020-01-07 09:47:57 -0500956static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
957 u8 *config_select)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400958{
Vincent Cheng1ece2fb2020-01-07 09:47:57 -0500959 return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
960 config_select, sizeof(u8));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400961}
962
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400963static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
964{
965 int err = 0;
966
967 switch (addr) {
Min Li7ea5fda2020-07-28 16:00:30 -0400968 case TOD0_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400969 SET_U16_LSB(idtcm->channel[0].output_mask, val);
970 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400971 case TOD0_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400972 SET_U16_MSB(idtcm->channel[0].output_mask, val);
973 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400974 case TOD1_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400975 SET_U16_LSB(idtcm->channel[1].output_mask, val);
976 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400977 case TOD1_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400978 SET_U16_MSB(idtcm->channel[1].output_mask, val);
979 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400980 case TOD2_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400981 SET_U16_LSB(idtcm->channel[2].output_mask, val);
982 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400983 case TOD2_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400984 SET_U16_MSB(idtcm->channel[2].output_mask, val);
985 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400986 case TOD3_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400987 SET_U16_LSB(idtcm->channel[3].output_mask, val);
988 break;
Min Li7ea5fda2020-07-28 16:00:30 -0400989 case TOD3_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400990 SET_U16_MSB(idtcm->channel[3].output_mask, val);
991 break;
992 default:
Min Li7ea5fda2020-07-28 16:00:30 -0400993 err = -EFAULT; /* Bad address */;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400994 break;
995 }
996
997 return err;
998}
999
Min Li7ea5fda2020-07-28 16:00:30 -04001000static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
1001{
1002 if (index >= MAX_TOD) {
1003 dev_err(&idtcm->client->dev, "ToD%d not supported\n", index);
1004 return -EINVAL;
1005 }
1006
1007 if (pll >= MAX_PLL) {
1008 dev_err(&idtcm->client->dev, "Pll%d not supported\n", pll);
1009 return -EINVAL;
1010 }
1011
1012 idtcm->channel[index].pll = pll;
1013
1014 return 0;
1015}
1016
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001017static int check_and_set_masks(struct idtcm *idtcm,
1018 u16 regaddr,
1019 u8 val)
1020{
1021 int err = 0;
1022
Min Li7ea5fda2020-07-28 16:00:30 -04001023 switch (regaddr) {
1024 case TOD_MASK_ADDR:
1025 if ((val & 0xf0) || !(val & 0x0f)) {
1026 dev_err(&idtcm->client->dev,
1027 "Invalid TOD mask 0x%hhx\n", val);
1028 err = -EINVAL;
1029 } else {
1030 idtcm->tod_mask = val;
1031 }
1032 break;
1033 case TOD0_PTP_PLL_ADDR:
1034 err = set_tod_ptp_pll(idtcm, 0, val);
1035 break;
1036 case TOD1_PTP_PLL_ADDR:
1037 err = set_tod_ptp_pll(idtcm, 1, val);
1038 break;
1039 case TOD2_PTP_PLL_ADDR:
1040 err = set_tod_ptp_pll(idtcm, 2, val);
1041 break;
1042 case TOD3_PTP_PLL_ADDR:
1043 err = set_tod_ptp_pll(idtcm, 3, val);
1044 break;
1045 default:
1046 err = set_pll_output_mask(idtcm, regaddr, val);
1047 break;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001048 }
1049
1050 return err;
1051}
1052
Min Li7ea5fda2020-07-28 16:00:30 -04001053static void display_pll_and_masks(struct idtcm *idtcm)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001054{
1055 u8 i;
1056 u8 mask;
1057
Min Li7ea5fda2020-07-28 16:00:30 -04001058 dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x\n", idtcm->tod_mask);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001059
Min Li7ea5fda2020-07-28 16:00:30 -04001060 for (i = 0; i < MAX_TOD; i++) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001061 mask = 1 << i;
1062
Min Li7ea5fda2020-07-28 16:00:30 -04001063 if (mask & idtcm->tod_mask)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001064 dev_dbg(&idtcm->client->dev,
Min Li7ea5fda2020-07-28 16:00:30 -04001065 "TOD%d pll = %d output_mask = 0x%04x\n",
1066 i, idtcm->channel[i].pll,
1067 idtcm->channel[i].output_mask);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001068 }
1069}
1070
1071static int idtcm_load_firmware(struct idtcm *idtcm,
1072 struct device *dev)
1073{
Min Li7ea5fda2020-07-28 16:00:30 -04001074 char fname[128] = FW_FILENAME;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001075 const struct firmware *fw;
1076 struct idtcm_fwrc *rec;
1077 u32 regaddr;
1078 int err;
1079 s32 len;
1080 u8 val;
1081 u8 loaddr;
1082
Min Li7ea5fda2020-07-28 16:00:30 -04001083 if (firmware) /* module parameter */
1084 snprintf(fname, sizeof(fname), "%s", firmware);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001085
Min Li7ea5fda2020-07-28 16:00:30 -04001086 dev_dbg(&idtcm->client->dev, "requesting firmware '%s'\n", fname);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001087
Min Li7ea5fda2020-07-28 16:00:30 -04001088 err = request_firmware(&fw, fname, dev);
1089
1090 if (err) {
1091 dev_err(&idtcm->client->dev,
1092 "Failed at line %d in func %s!\n",
1093 __LINE__,
1094 __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001095 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04001096 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001097
1098 dev_dbg(&idtcm->client->dev, "firmware size %zu bytes\n", fw->size);
1099
1100 rec = (struct idtcm_fwrc *) fw->data;
1101
1102 if (fw->size > 0)
1103 idtcm_state_machine_reset(idtcm);
1104
1105 for (len = fw->size; len > 0; len -= sizeof(*rec)) {
1106
1107 if (rec->reserved) {
1108 dev_err(&idtcm->client->dev,
1109 "bad firmware, reserved field non-zero\n");
1110 err = -EINVAL;
1111 } else {
1112 regaddr = rec->hiaddr << 8;
1113 regaddr |= rec->loaddr;
1114
1115 val = rec->value;
1116 loaddr = rec->loaddr;
1117
1118 rec++;
1119
1120 err = check_and_set_masks(idtcm, regaddr, val);
1121 }
1122
Min Li7ea5fda2020-07-28 16:00:30 -04001123 if (err != -EINVAL) {
1124 err = 0;
1125
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001126 /* Top (status registers) and bottom are read-only */
1127 if ((regaddr < GPIO_USER_CONTROL)
1128 || (regaddr >= SCRATCH))
1129 continue;
1130
1131 /* Page size 128, last 4 bytes of page skipped */
1132 if (((loaddr > 0x7b) && (loaddr <= 0x7f))
Yang Yingliang3e144622020-04-24 20:52:26 +08001133 || loaddr > 0xfb)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001134 continue;
1135
1136 err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
1137 }
1138
1139 if (err)
1140 goto out;
1141 }
1142
Min Li7ea5fda2020-07-28 16:00:30 -04001143 display_pll_and_masks(idtcm);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001144
1145out:
1146 release_firmware(fw);
1147 return err;
1148}
1149
Min Li7ea5fda2020-07-28 16:00:30 -04001150static int idtcm_output_enable(struct idtcm_channel *channel,
1151 bool enable, unsigned int outn)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001152{
1153 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001154 int err;
Min Li7ea5fda2020-07-28 16:00:30 -04001155 u8 val;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001156
Min Li7ea5fda2020-07-28 16:00:30 -04001157 err = idtcm_read(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
1158 OUT_CTRL_1, &val, sizeof(val));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001159
1160 if (err)
1161 return err;
1162
1163 if (enable)
1164 val |= SQUELCH_DISABLE;
1165 else
1166 val &= ~SQUELCH_DISABLE;
1167
Min Li7ea5fda2020-07-28 16:00:30 -04001168 return idtcm_write(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
1169 OUT_CTRL_1, &val, sizeof(val));
1170}
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001171
Min Li7ea5fda2020-07-28 16:00:30 -04001172static int idtcm_output_mask_enable(struct idtcm_channel *channel,
1173 bool enable)
1174{
1175 u16 mask;
1176 int err;
1177 u8 outn;
1178
1179 mask = channel->output_mask;
1180 outn = 0;
1181
1182 while (mask) {
1183
1184 if (mask & 0x1) {
1185
1186 err = idtcm_output_enable(channel, enable, outn);
1187
1188 if (err)
1189 return err;
1190 }
1191
1192 mask >>= 0x1;
1193 outn++;
1194 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001195
1196 return 0;
1197}
1198
Min Li7ea5fda2020-07-28 16:00:30 -04001199static int idtcm_perout_enable(struct idtcm_channel *channel,
1200 bool enable,
1201 struct ptp_perout_request *perout)
1202{
1203 unsigned int flags = perout->flags;
1204
1205 if (flags == PEROUT_ENABLE_OUTPUT_MASK)
1206 return idtcm_output_mask_enable(channel, enable);
1207
1208 /* Enable/disable individual output instead */
1209 return idtcm_output_enable(channel, enable, perout->index);
1210}
1211
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001212static int idtcm_set_pll_mode(struct idtcm_channel *channel,
1213 enum pll_mode pll_mode)
1214{
1215 struct idtcm *idtcm = channel->idtcm;
1216 int err;
1217 u8 dpll_mode;
1218
1219 err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
1220 &dpll_mode, sizeof(dpll_mode));
1221 if (err)
1222 return err;
1223
1224 dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
1225
1226 dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
1227
1228 channel->pll_mode = pll_mode;
1229
1230 err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
1231 &dpll_mode, sizeof(dpll_mode));
1232 if (err)
1233 return err;
1234
1235 return 0;
1236}
1237
1238/* PTP Hardware Clock interface */
1239
Vincent Cheng425d2b12020-05-01 23:35:38 -04001240/**
1241 * @brief Maximum absolute value for write phase offset in picoseconds
1242 *
1243 * Destination signed register is 32-bit register in resolution of 50ps
1244 *
1245 * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
1246 */
1247static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1248{
1249 struct idtcm *idtcm = channel->idtcm;
1250
1251 int err;
1252 u8 i;
1253 u8 buf[4] = {0};
1254 s32 phase_50ps;
1255 s64 offset_ps;
1256
1257 if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
1258
1259 err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
1260
1261 if (err)
1262 return err;
1263
1264 channel->write_phase_ready = 0;
1265
1266 ptp_schedule_worker(channel->ptp_clock,
1267 msecs_to_jiffies(WR_PHASE_SETUP_MS));
1268 }
1269
1270 if (!channel->write_phase_ready)
1271 delta_ns = 0;
1272
1273 offset_ps = (s64)delta_ns * 1000;
1274
1275 /*
1276 * Check for 32-bit signed max * 50:
1277 *
1278 * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
1279 */
1280 if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
1281 offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
1282 else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
1283 offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
1284
1285 phase_50ps = DIV_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
1286
1287 for (i = 0; i < 4; i++) {
1288 buf[i] = phase_50ps & 0xff;
1289 phase_50ps >>= 8;
1290 }
1291
1292 err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1293 buf, sizeof(buf));
1294
1295 return err;
1296}
1297
Min Li7ea5fda2020-07-28 16:00:30 -04001298static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001299{
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001300 struct idtcm *idtcm = channel->idtcm;
1301 u8 i;
1302 bool neg_adj = 0;
1303 int err;
1304 u8 buf[6] = {0};
1305 s64 fcw;
1306
1307 if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) {
1308 err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
1309 if (err)
1310 return err;
1311 }
1312
1313 /*
1314 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
1315 *
1316 * adjfreq:
1317 * ppb * 10^9
1318 * FCW = ----------
1319 * 111
1320 *
1321 * adjfine:
1322 * ppm_16 * 5^12
1323 * FCW = -------------
1324 * 111 * 2^4
1325 */
Min Li7ea5fda2020-07-28 16:00:30 -04001326 if (scaled_ppm < 0) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001327 neg_adj = 1;
Min Li7ea5fda2020-07-28 16:00:30 -04001328 scaled_ppm = -scaled_ppm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001329 }
1330
1331 /* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
Min Li7ea5fda2020-07-28 16:00:30 -04001332 fcw = scaled_ppm * 244140625ULL;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001333
Min Li7ea5fda2020-07-28 16:00:30 -04001334 fcw = div_u64(fcw, 1776);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001335
1336 if (neg_adj)
1337 fcw = -fcw;
1338
1339 for (i = 0; i < 6; i++) {
1340 buf[i] = fcw & 0xff;
1341 fcw >>= 8;
1342 }
1343
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001344 err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
1345 buf, sizeof(buf));
1346
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001347 return err;
1348}
1349
1350static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
1351{
1352 struct idtcm_channel *channel =
1353 container_of(ptp, struct idtcm_channel, caps);
1354 struct idtcm *idtcm = channel->idtcm;
1355 int err;
1356
1357 mutex_lock(&idtcm->reg_lock);
1358
1359 err = _idtcm_gettime(channel, ts);
1360
Min Li7ea5fda2020-07-28 16:00:30 -04001361 if (err)
1362 dev_err(&idtcm->client->dev,
1363 "Failed at line %d in func %s!\n",
1364 __LINE__,
1365 __func__);
1366
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001367 mutex_unlock(&idtcm->reg_lock);
1368
1369 return err;
1370}
1371
1372static int idtcm_settime(struct ptp_clock_info *ptp,
1373 const struct timespec64 *ts)
1374{
1375 struct idtcm_channel *channel =
1376 container_of(ptp, struct idtcm_channel, caps);
1377 struct idtcm *idtcm = channel->idtcm;
1378 int err;
1379
1380 mutex_lock(&idtcm->reg_lock);
1381
1382 err = _idtcm_settime(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
1383
Min Li7ea5fda2020-07-28 16:00:30 -04001384 if (err)
1385 dev_err(&idtcm->client->dev,
1386 "Failed at line %d in func %s!\n",
1387 __LINE__,
1388 __func__);
1389
1390 mutex_unlock(&idtcm->reg_lock);
1391
1392 return err;
1393}
1394
1395static int idtcm_settime_v487(struct ptp_clock_info *ptp,
1396 const struct timespec64 *ts)
1397{
1398 struct idtcm_channel *channel =
1399 container_of(ptp, struct idtcm_channel, caps);
1400 struct idtcm *idtcm = channel->idtcm;
1401 int err;
1402
1403 mutex_lock(&idtcm->reg_lock);
1404
1405 err = _idtcm_settime_v487(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
1406
1407 if (err)
1408 dev_err(&idtcm->client->dev,
1409 "Failed at line %d in func %s!\n",
1410 __LINE__,
1411 __func__);
1412
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001413 mutex_unlock(&idtcm->reg_lock);
1414
1415 return err;
1416}
1417
1418static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
1419{
1420 struct idtcm_channel *channel =
1421 container_of(ptp, struct idtcm_channel, caps);
1422 struct idtcm *idtcm = channel->idtcm;
1423 int err;
1424
1425 mutex_lock(&idtcm->reg_lock);
1426
1427 err = _idtcm_adjtime(channel, delta);
1428
Min Li7ea5fda2020-07-28 16:00:30 -04001429 if (err)
1430 dev_err(&idtcm->client->dev,
1431 "Failed at line %d in func %s!\n",
1432 __LINE__,
1433 __func__);
1434
1435 mutex_unlock(&idtcm->reg_lock);
1436
1437 return err;
1438}
1439
1440static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
1441{
1442 struct idtcm_channel *channel =
1443 container_of(ptp, struct idtcm_channel, caps);
1444 struct idtcm *idtcm = channel->idtcm;
1445 struct timespec64 ts;
1446 enum scsr_tod_write_type_sel type;
1447 int err;
1448
1449 if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_V487) {
1450 err = idtcm_do_phase_pull_in(channel, delta, 0);
1451 if (err)
1452 dev_err(&idtcm->client->dev,
1453 "Failed at line %d in func %s!\n",
1454 __LINE__,
1455 __func__);
1456 return err;
1457 }
1458
1459 if (delta >= 0) {
1460 ts = ns_to_timespec64(delta);
1461 type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
1462 } else {
1463 ts = ns_to_timespec64(-delta);
1464 type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
1465 }
1466
1467 mutex_lock(&idtcm->reg_lock);
1468
1469 err = _idtcm_settime_v487(channel, &ts, type);
1470
1471 if (err)
1472 dev_err(&idtcm->client->dev,
1473 "Failed at line %d in func %s!\n",
1474 __LINE__,
1475 __func__);
1476
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001477 mutex_unlock(&idtcm->reg_lock);
1478
1479 return err;
1480}
1481
Vincent Cheng425d2b12020-05-01 23:35:38 -04001482static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1483{
1484 struct idtcm_channel *channel =
1485 container_of(ptp, struct idtcm_channel, caps);
1486
1487 struct idtcm *idtcm = channel->idtcm;
1488
1489 int err;
1490
1491 mutex_lock(&idtcm->reg_lock);
1492
1493 err = _idtcm_adjphase(channel, delta);
1494
Min Li7ea5fda2020-07-28 16:00:30 -04001495 if (err)
1496 dev_err(&idtcm->client->dev,
1497 "Failed at line %d in func %s!\n",
1498 __LINE__,
1499 __func__);
1500
1501 mutex_unlock(&idtcm->reg_lock);
1502
1503 return err;
1504}
1505
1506static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
1507{
1508 struct idtcm_channel *channel =
1509 container_of(ptp, struct idtcm_channel, caps);
1510
1511 struct idtcm *idtcm = channel->idtcm;
1512
1513 int err;
1514
1515 mutex_lock(&idtcm->reg_lock);
1516
1517 err = _idtcm_adjfine(channel, scaled_ppm);
1518
1519 if (err)
1520 dev_err(&idtcm->client->dev,
1521 "Failed at line %d in func %s!\n",
1522 __LINE__,
1523 __func__);
1524
Vincent Cheng425d2b12020-05-01 23:35:38 -04001525 mutex_unlock(&idtcm->reg_lock);
1526
1527 return err;
1528}
1529
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001530static int idtcm_enable(struct ptp_clock_info *ptp,
1531 struct ptp_clock_request *rq, int on)
1532{
Min Li7ea5fda2020-07-28 16:00:30 -04001533 int err;
1534
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001535 struct idtcm_channel *channel =
1536 container_of(ptp, struct idtcm_channel, caps);
1537
1538 switch (rq->type) {
1539 case PTP_CLK_REQ_PEROUT:
Min Li7ea5fda2020-07-28 16:00:30 -04001540 if (!on) {
1541 err = idtcm_perout_enable(channel, false, &rq->perout);
1542 if (err)
1543 dev_err(&channel->idtcm->client->dev,
1544 "Failed at line %d in func %s!\n",
1545 __LINE__,
1546 __func__);
1547 return err;
1548 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001549
1550 /* Only accept a 1-PPS aligned to the second. */
1551 if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
1552 rq->perout.period.nsec)
1553 return -ERANGE;
1554
Min Li7ea5fda2020-07-28 16:00:30 -04001555 err = idtcm_perout_enable(channel, true, &rq->perout);
1556 if (err)
1557 dev_err(&channel->idtcm->client->dev,
1558 "Failed at line %d in func %s!\n",
1559 __LINE__,
1560 __func__);
1561 return err;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001562 default:
1563 break;
1564 }
1565
1566 return -EOPNOTSUPP;
1567}
1568
Min Li7ea5fda2020-07-28 16:00:30 -04001569static int _enable_pll_tod_sync(struct idtcm *idtcm,
1570 u8 pll,
1571 u8 sync_src,
1572 u8 qn,
1573 u8 qn_plus_1)
1574{
1575 int err;
1576 u8 val;
1577 u16 dpll;
1578 u16 out0 = 0, out1 = 0;
1579
1580 if ((qn == 0) && (qn_plus_1 == 0))
1581 return 0;
1582
1583 switch (pll) {
1584 case 0:
1585 dpll = DPLL_0;
1586 if (qn)
1587 out0 = OUTPUT_0;
1588 if (qn_plus_1)
1589 out1 = OUTPUT_1;
1590 break;
1591 case 1:
1592 dpll = DPLL_1;
1593 if (qn)
1594 out0 = OUTPUT_2;
1595 if (qn_plus_1)
1596 out1 = OUTPUT_3;
1597 break;
1598 case 2:
1599 dpll = DPLL_2;
1600 if (qn)
1601 out0 = OUTPUT_4;
1602 if (qn_plus_1)
1603 out1 = OUTPUT_5;
1604 break;
1605 case 3:
1606 dpll = DPLL_3;
1607 if (qn)
1608 out0 = OUTPUT_6;
1609 if (qn_plus_1)
1610 out1 = OUTPUT_7;
1611 break;
1612 case 4:
1613 dpll = DPLL_4;
1614 if (qn)
1615 out0 = OUTPUT_8;
1616 break;
1617 case 5:
1618 dpll = DPLL_5;
1619 if (qn)
1620 out0 = OUTPUT_9;
1621 if (qn_plus_1)
1622 out1 = OUTPUT_8;
1623 break;
1624 case 6:
1625 dpll = DPLL_6;
1626 if (qn)
1627 out0 = OUTPUT_10;
1628 if (qn_plus_1)
1629 out1 = OUTPUT_11;
1630 break;
1631 case 7:
1632 dpll = DPLL_7;
1633 if (qn)
1634 out0 = OUTPUT_11;
1635 break;
1636 default:
1637 return -EINVAL;
1638 }
1639
1640 /*
1641 * Enable OUTPUT OUT_SYNC.
1642 */
1643 if (out0) {
1644 err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
1645
1646 if (err)
1647 return err;
1648
1649 val &= ~OUT_SYNC_DISABLE;
1650
1651 err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
1652
1653 if (err)
1654 return err;
1655 }
1656
1657 if (out1) {
1658 err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
1659
1660 if (err)
1661 return err;
1662
1663 val &= ~OUT_SYNC_DISABLE;
1664
1665 err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
1666
1667 if (err)
1668 return err;
1669 }
1670
1671 /* enable dpll sync tod pps, must be set before dpll_mode */
1672 err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
1673 if (err)
1674 return err;
1675
1676 val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
1677 val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
1678 val |= TOD_SYNC_EN;
1679
1680 return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
1681}
1682
1683static int idtcm_enable_tod_sync(struct idtcm_channel *channel)
1684{
1685 struct idtcm *idtcm = channel->idtcm;
1686
1687 u8 pll;
1688 u8 sync_src;
1689 u8 qn;
1690 u8 qn_plus_1;
1691 u8 cfg;
1692 int err = 0;
1693 u16 output_mask = channel->output_mask;
1694 u8 out8_mux = 0;
1695 u8 out11_mux = 0;
1696 u8 temp;
1697
1698 /*
1699 * set tod_out_sync_enable to 0.
1700 */
1701 err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1702 if (err)
1703 return err;
1704
1705 cfg &= ~TOD_OUT_SYNC_ENABLE;
1706
1707 err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1708 if (err)
1709 return err;
1710
1711 switch (channel->tod_n) {
1712 case TOD_0:
1713 sync_src = 0;
1714 break;
1715 case TOD_1:
1716 sync_src = 1;
1717 break;
1718 case TOD_2:
1719 sync_src = 2;
1720 break;
1721 case TOD_3:
1722 sync_src = 3;
1723 break;
1724 default:
1725 return -EINVAL;
1726 }
1727
1728 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
1729 &temp, sizeof(temp));
1730 if (err)
1731 return err;
1732
1733 if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
1734 Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
1735 out8_mux = 1;
1736
1737 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
1738 &temp, sizeof(temp));
1739 if (err)
1740 return err;
1741
1742 if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
1743 Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
1744 out11_mux = 1;
1745
1746 for (pll = 0; pll < 8; pll++) {
1747 qn = 0;
1748 qn_plus_1 = 0;
1749
1750 if (pll < 4) {
1751 /* First 4 pll has 2 outputs */
1752 qn = output_mask & 0x1;
1753 output_mask = output_mask >> 1;
1754 qn_plus_1 = output_mask & 0x1;
1755 output_mask = output_mask >> 1;
1756 } else if (pll == 4) {
1757 if (out8_mux == 0) {
1758 qn = output_mask & 0x1;
1759 output_mask = output_mask >> 1;
1760 }
1761 } else if (pll == 5) {
1762 if (out8_mux) {
1763 qn_plus_1 = output_mask & 0x1;
1764 output_mask = output_mask >> 1;
1765 }
1766 qn = output_mask & 0x1;
1767 output_mask = output_mask >> 1;
1768 } else if (pll == 6) {
1769 qn = output_mask & 0x1;
1770 output_mask = output_mask >> 1;
1771 if (out11_mux) {
1772 qn_plus_1 = output_mask & 0x1;
1773 output_mask = output_mask >> 1;
1774 }
1775 } else if (pll == 7) {
1776 if (out11_mux == 0) {
1777 qn = output_mask & 0x1;
1778 output_mask = output_mask >> 1;
1779 }
1780 }
1781
1782 if ((qn != 0) || (qn_plus_1 != 0))
1783 err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
1784 qn_plus_1);
1785
1786 if (err)
1787 return err;
1788 }
1789
1790 return err;
1791}
1792
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001793static int idtcm_enable_tod(struct idtcm_channel *channel)
1794{
1795 struct idtcm *idtcm = channel->idtcm;
1796 struct timespec64 ts = {0, 0};
1797 u8 cfg;
1798 int err;
1799
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001800 /*
1801 * Start the TOD clock ticking.
1802 */
1803 err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1804 if (err)
1805 return err;
1806
1807 cfg |= TOD_ENABLE;
1808
1809 err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1810 if (err)
1811 return err;
1812
1813 return _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
1814}
1815
1816static void idtcm_display_version_info(struct idtcm *idtcm)
1817{
1818 u8 major;
1819 u8 minor;
1820 u8 hotfix;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001821 u16 product_id;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001822 u8 hw_rev_id;
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001823 u8 config_select;
1824 char *fmt = "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d\n";
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001825
1826 idtcm_read_major_release(idtcm, &major);
1827 idtcm_read_minor_release(idtcm, &minor);
1828 idtcm_read_hotfix_release(idtcm, &hotfix);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001829
1830 idtcm_read_product_id(idtcm, &product_id);
1831 idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001832
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001833 idtcm_read_otp_scsr_config_select(idtcm, &config_select);
1834
Min Li7ea5fda2020-07-28 16:00:30 -04001835 snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
1836 major, minor, hotfix);
1837
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001838 dev_info(&idtcm->client->dev, fmt, major, minor, hotfix,
1839 product_id, hw_rev_id, config_select);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001840}
1841
Min Li7ea5fda2020-07-28 16:00:30 -04001842static const struct ptp_clock_info idtcm_caps_v487 = {
1843 .owner = THIS_MODULE,
1844 .max_adj = 244000,
1845 .n_per_out = 12,
1846 .adjphase = &idtcm_adjphase,
1847 .adjfine = &idtcm_adjfine,
1848 .adjtime = &idtcm_adjtime_v487,
1849 .gettime64 = &idtcm_gettime,
1850 .settime64 = &idtcm_settime_v487,
1851 .enable = &idtcm_enable,
1852 .do_aux_work = &set_write_phase_ready,
1853};
1854
Julia Lawall6485f9a2020-01-01 08:43:31 +01001855static const struct ptp_clock_info idtcm_caps = {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001856 .owner = THIS_MODULE,
1857 .max_adj = 244000,
Min Li7ea5fda2020-07-28 16:00:30 -04001858 .n_per_out = 12,
Vincent Cheng425d2b12020-05-01 23:35:38 -04001859 .adjphase = &idtcm_adjphase,
Min Li7ea5fda2020-07-28 16:00:30 -04001860 .adjfine = &idtcm_adjfine,
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001861 .adjtime = &idtcm_adjtime,
1862 .gettime64 = &idtcm_gettime,
1863 .settime64 = &idtcm_settime,
1864 .enable = &idtcm_enable,
Vincent Cheng425d2b12020-05-01 23:35:38 -04001865 .do_aux_work = &set_write_phase_ready,
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001866};
1867
Min Li7ea5fda2020-07-28 16:00:30 -04001868static int configure_channel_pll(struct idtcm_channel *channel)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001869{
Min Li7ea5fda2020-07-28 16:00:30 -04001870 int err = 0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001871
Min Li7ea5fda2020-07-28 16:00:30 -04001872 switch (channel->pll) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001873 case 0:
1874 channel->dpll_freq = DPLL_FREQ_0;
1875 channel->dpll_n = DPLL_0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001876 channel->hw_dpll_n = HW_DPLL_0;
1877 channel->dpll_phase = DPLL_PHASE_0;
1878 channel->dpll_ctrl_n = DPLL_CTRL_0;
1879 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
1880 break;
1881 case 1:
1882 channel->dpll_freq = DPLL_FREQ_1;
1883 channel->dpll_n = DPLL_1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001884 channel->hw_dpll_n = HW_DPLL_1;
1885 channel->dpll_phase = DPLL_PHASE_1;
1886 channel->dpll_ctrl_n = DPLL_CTRL_1;
1887 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
1888 break;
1889 case 2:
1890 channel->dpll_freq = DPLL_FREQ_2;
1891 channel->dpll_n = DPLL_2;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001892 channel->hw_dpll_n = HW_DPLL_2;
1893 channel->dpll_phase = DPLL_PHASE_2;
1894 channel->dpll_ctrl_n = DPLL_CTRL_2;
1895 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
1896 break;
1897 case 3:
1898 channel->dpll_freq = DPLL_FREQ_3;
1899 channel->dpll_n = DPLL_3;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001900 channel->hw_dpll_n = HW_DPLL_3;
1901 channel->dpll_phase = DPLL_PHASE_3;
1902 channel->dpll_ctrl_n = DPLL_CTRL_3;
1903 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
1904 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001905 case 4:
1906 channel->dpll_freq = DPLL_FREQ_4;
1907 channel->dpll_n = DPLL_4;
1908 channel->hw_dpll_n = HW_DPLL_4;
1909 channel->dpll_phase = DPLL_PHASE_4;
1910 channel->dpll_ctrl_n = DPLL_CTRL_4;
1911 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
1912 break;
1913 case 5:
1914 channel->dpll_freq = DPLL_FREQ_5;
1915 channel->dpll_n = DPLL_5;
1916 channel->hw_dpll_n = HW_DPLL_5;
1917 channel->dpll_phase = DPLL_PHASE_5;
1918 channel->dpll_ctrl_n = DPLL_CTRL_5;
1919 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
1920 break;
1921 case 6:
1922 channel->dpll_freq = DPLL_FREQ_6;
1923 channel->dpll_n = DPLL_6;
1924 channel->hw_dpll_n = HW_DPLL_6;
1925 channel->dpll_phase = DPLL_PHASE_6;
1926 channel->dpll_ctrl_n = DPLL_CTRL_6;
1927 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
1928 break;
1929 case 7:
1930 channel->dpll_freq = DPLL_FREQ_7;
1931 channel->dpll_n = DPLL_7;
1932 channel->hw_dpll_n = HW_DPLL_7;
1933 channel->dpll_phase = DPLL_PHASE_7;
1934 channel->dpll_ctrl_n = DPLL_CTRL_7;
1935 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
1936 break;
1937 default:
1938 err = -EINVAL;
1939 }
1940
1941 return err;
1942}
1943
1944static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
1945{
1946 struct idtcm_channel *channel;
1947 int err;
1948
1949 if (!(index < MAX_TOD))
1950 return -EINVAL;
1951
1952 channel = &idtcm->channel[index];
1953
1954 /* Set pll addresses */
1955 err = configure_channel_pll(channel);
1956 if (err)
1957 return err;
1958
1959 /* Set tod addresses */
1960 switch (index) {
1961 case 0:
1962 channel->tod_read_primary = TOD_READ_PRIMARY_0;
1963 channel->tod_write = TOD_WRITE_0;
1964 channel->tod_n = TOD_0;
1965 break;
1966 case 1:
1967 channel->tod_read_primary = TOD_READ_PRIMARY_1;
1968 channel->tod_write = TOD_WRITE_1;
1969 channel->tod_n = TOD_1;
1970 break;
1971 case 2:
1972 channel->tod_read_primary = TOD_READ_PRIMARY_2;
1973 channel->tod_write = TOD_WRITE_2;
1974 channel->tod_n = TOD_2;
1975 break;
1976 case 3:
1977 channel->tod_read_primary = TOD_READ_PRIMARY_3;
1978 channel->tod_write = TOD_WRITE_3;
1979 channel->tod_n = TOD_3;
1980 break;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001981 default:
1982 return -EINVAL;
1983 }
1984
1985 channel->idtcm = idtcm;
1986
Min Li7ea5fda2020-07-28 16:00:30 -04001987 if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
1988 channel->caps = idtcm_caps_v487;
1989 else
1990 channel->caps = idtcm_caps;
1991
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001992 snprintf(channel->caps.name, sizeof(channel->caps.name),
Min Li7ea5fda2020-07-28 16:00:30 -04001993 "IDT CM TOD%u", index);
1994
1995 if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) {
1996 err = idtcm_enable_tod_sync(channel);
1997 if (err) {
1998 dev_err(&idtcm->client->dev,
1999 "Failed at line %d in func %s!\n",
2000 __LINE__,
2001 __func__);
2002 return err;
2003 }
2004 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002005
2006 err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
Min Li7ea5fda2020-07-28 16:00:30 -04002007 if (err) {
2008 dev_err(&idtcm->client->dev,
2009 "Failed at line %d in func %s!\n",
2010 __LINE__,
2011 __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002012 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04002013 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002014
2015 err = idtcm_enable_tod(channel);
Min Li7ea5fda2020-07-28 16:00:30 -04002016 if (err) {
2017 dev_err(&idtcm->client->dev,
2018 "Failed at line %d in func %s!\n",
2019 __LINE__,
2020 __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002021 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04002022 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002023
2024 channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
2025
2026 if (IS_ERR(channel->ptp_clock)) {
2027 err = PTR_ERR(channel->ptp_clock);
2028 channel->ptp_clock = NULL;
2029 return err;
2030 }
2031
2032 if (!channel->ptp_clock)
2033 return -ENOTSUPP;
2034
Vincent Cheng425d2b12020-05-01 23:35:38 -04002035 channel->write_phase_ready = 0;
2036
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002037 dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
2038 index, channel->ptp_clock->index);
2039
2040 return 0;
2041}
2042
2043static void ptp_clock_unregister_all(struct idtcm *idtcm)
2044{
2045 u8 i;
2046 struct idtcm_channel *channel;
2047
Min Li7ea5fda2020-07-28 16:00:30 -04002048 for (i = 0; i < MAX_TOD; i++) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002049
2050 channel = &idtcm->channel[i];
2051
2052 if (channel->ptp_clock)
2053 ptp_clock_unregister(channel->ptp_clock);
2054 }
2055}
2056
2057static void set_default_masks(struct idtcm *idtcm)
2058{
Min Li7ea5fda2020-07-28 16:00:30 -04002059 idtcm->tod_mask = DEFAULT_TOD_MASK;
2060
2061 idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
2062 idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
2063 idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
2064 idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002065
2066 idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
2067 idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
2068 idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
2069 idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
2070}
2071
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002072static int idtcm_probe(struct i2c_client *client,
2073 const struct i2c_device_id *id)
2074{
2075 struct idtcm *idtcm;
2076 int err;
2077 u8 i;
Min Li7ea5fda2020-07-28 16:00:30 -04002078 char *fmt = "Failed at %d in line %s with channel output %d!\n";
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002079
2080 /* Unused for now */
2081 (void)id;
2082
2083 idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
2084
2085 if (!idtcm)
2086 return -ENOMEM;
2087
2088 idtcm->client = client;
2089 idtcm->page_offset = 0xff;
2090 idtcm->calculate_overhead_flag = 0;
2091
2092 set_default_masks(idtcm);
2093
2094 mutex_init(&idtcm->reg_lock);
2095 mutex_lock(&idtcm->reg_lock);
2096
2097 idtcm_display_version_info(idtcm);
2098
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002099 err = idtcm_load_firmware(idtcm, &client->dev);
2100
2101 if (err)
2102 dev_warn(&idtcm->client->dev,
2103 "loading firmware failed with %d\n", err);
2104
Min Li7ea5fda2020-07-28 16:00:30 -04002105 if (idtcm->tod_mask) {
2106 for (i = 0; i < MAX_TOD; i++) {
2107 if (idtcm->tod_mask & (1 << i)) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002108 err = idtcm_enable_channel(idtcm, i);
Min Li7ea5fda2020-07-28 16:00:30 -04002109 if (err) {
2110 dev_err(&idtcm->client->dev,
2111 fmt,
2112 __LINE__,
2113 __func__,
2114 i);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002115 break;
Min Li7ea5fda2020-07-28 16:00:30 -04002116 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002117 }
2118 }
2119 } else {
2120 dev_err(&idtcm->client->dev,
2121 "no PLLs flagged as PHCs, nothing to do\n");
2122 err = -ENODEV;
2123 }
2124
2125 mutex_unlock(&idtcm->reg_lock);
2126
2127 if (err) {
2128 ptp_clock_unregister_all(idtcm);
2129 return err;
2130 }
2131
2132 i2c_set_clientdata(client, idtcm);
2133
2134 return 0;
2135}
2136
2137static int idtcm_remove(struct i2c_client *client)
2138{
2139 struct idtcm *idtcm = i2c_get_clientdata(client);
2140
2141 ptp_clock_unregister_all(idtcm);
2142
2143 mutex_destroy(&idtcm->reg_lock);
2144
2145 return 0;
2146}
2147
2148#ifdef CONFIG_OF
2149static const struct of_device_id idtcm_dt_id[] = {
2150 { .compatible = "idt,8a34000" },
2151 { .compatible = "idt,8a34001" },
2152 { .compatible = "idt,8a34002" },
2153 { .compatible = "idt,8a34003" },
2154 { .compatible = "idt,8a34004" },
2155 { .compatible = "idt,8a34005" },
2156 { .compatible = "idt,8a34006" },
2157 { .compatible = "idt,8a34007" },
2158 { .compatible = "idt,8a34008" },
2159 { .compatible = "idt,8a34009" },
2160 { .compatible = "idt,8a34010" },
2161 { .compatible = "idt,8a34011" },
2162 { .compatible = "idt,8a34012" },
2163 { .compatible = "idt,8a34013" },
2164 { .compatible = "idt,8a34014" },
2165 { .compatible = "idt,8a34015" },
2166 { .compatible = "idt,8a34016" },
2167 { .compatible = "idt,8a34017" },
2168 { .compatible = "idt,8a34018" },
2169 { .compatible = "idt,8a34019" },
2170 { .compatible = "idt,8a34040" },
2171 { .compatible = "idt,8a34041" },
2172 { .compatible = "idt,8a34042" },
2173 { .compatible = "idt,8a34043" },
2174 { .compatible = "idt,8a34044" },
2175 { .compatible = "idt,8a34045" },
2176 { .compatible = "idt,8a34046" },
2177 { .compatible = "idt,8a34047" },
2178 { .compatible = "idt,8a34048" },
2179 { .compatible = "idt,8a34049" },
2180 {},
2181};
2182MODULE_DEVICE_TABLE(of, idtcm_dt_id);
2183#endif
2184
2185static const struct i2c_device_id idtcm_i2c_id[] = {
2186 { "8a34000" },
2187 { "8a34001" },
2188 { "8a34002" },
2189 { "8a34003" },
2190 { "8a34004" },
2191 { "8a34005" },
2192 { "8a34006" },
2193 { "8a34007" },
2194 { "8a34008" },
2195 { "8a34009" },
2196 { "8a34010" },
2197 { "8a34011" },
2198 { "8a34012" },
2199 { "8a34013" },
2200 { "8a34014" },
2201 { "8a34015" },
2202 { "8a34016" },
2203 { "8a34017" },
2204 { "8a34018" },
2205 { "8a34019" },
2206 { "8a34040" },
2207 { "8a34041" },
2208 { "8a34042" },
2209 { "8a34043" },
2210 { "8a34044" },
2211 { "8a34045" },
2212 { "8a34046" },
2213 { "8a34047" },
2214 { "8a34048" },
2215 { "8a34049" },
2216 {},
2217};
2218MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
2219
2220static struct i2c_driver idtcm_driver = {
2221 .driver = {
2222 .of_match_table = of_match_ptr(idtcm_dt_id),
2223 .name = "idtcm",
2224 },
2225 .probe = idtcm_probe,
2226 .remove = idtcm_remove,
2227 .id_table = idtcm_i2c_id,
2228};
2229
2230module_i2c_driver(idtcm_driver);