blob: 241bff049b7717808ba0f2a16b0725d04b4824f6 [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
Min Li251f4fe2020-12-08 10:41:54 -050036static int contains_full_configuration(const struct firmware *fw)
37{
38 s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES;
39 struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data;
40 s32 count = 0;
41 u16 regaddr;
42 u8 loaddr;
43 s32 len;
44
45 /* If the firmware contains 'full configuration' SM_RESET can be used
46 * to ensure proper configuration.
47 *
48 * Full configuration is defined as the number of programmable
49 * bytes within the configuration range minus page offset addr range.
50 */
51 for (len = fw->size; len > 0; len -= sizeof(*rec)) {
52 regaddr = rec->hiaddr << 8;
53 regaddr |= rec->loaddr;
54
55 loaddr = rec->loaddr;
56
57 rec++;
58
59 /* Top (status registers) and bottom are read-only */
60 if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH)
61 continue;
62
63 /* Page size 128, last 4 bytes of page skipped */
64 if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
65 continue;
66
67 count++;
68 }
69
70 return (count >= full_count);
71}
72
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -040073static int char_array_to_timespec(u8 *buf,
74 u8 count,
75 struct timespec64 *ts)
76{
77 u8 i;
78 u64 nsec;
79 time64_t sec;
80
81 if (count < TOD_BYTE_COUNT)
82 return 1;
83
84 /* Sub-nanoseconds are in buf[0]. */
85 nsec = buf[4];
86 for (i = 0; i < 3; i++) {
87 nsec <<= 8;
88 nsec |= buf[3 - i];
89 }
90
91 sec = buf[10];
92 for (i = 0; i < 5; i++) {
93 sec <<= 8;
94 sec |= buf[9 - i];
95 }
96
97 ts->tv_sec = sec;
98 ts->tv_nsec = nsec;
99
100 return 0;
101}
102
103static int timespec_to_char_array(struct timespec64 const *ts,
104 u8 *buf,
105 u8 count)
106{
107 u8 i;
108 s32 nsec;
109 time64_t sec;
110
111 if (count < TOD_BYTE_COUNT)
112 return 1;
113
114 nsec = ts->tv_nsec;
115 sec = ts->tv_sec;
116
117 /* Sub-nanoseconds are in buf[0]. */
118 buf[0] = 0;
119 for (i = 1; i < 5; i++) {
120 buf[i] = nsec & 0xff;
121 nsec >>= 8;
122 }
123
124 for (i = 5; i < TOD_BYTE_COUNT; i++) {
125
126 buf[i] = sec & 0xff;
127 sec >>= 8;
128 }
129
130 return 0;
131}
132
Min Li3cb2e6d2020-11-24 21:58:35 -0500133static int idtcm_strverscmp(const char *version1, const char *version2)
Min Li7ea5fda2020-07-28 16:00:30 -0400134{
Min Li3cb2e6d2020-11-24 21:58:35 -0500135 u8 ver1[3], ver2[3];
136 int i;
Min Li7ea5fda2020-07-28 16:00:30 -0400137
Min Li3cb2e6d2020-11-24 21:58:35 -0500138 if (sscanf(version1, "%hhu.%hhu.%hhu",
139 &ver1[0], &ver1[1], &ver1[2]) != 3)
140 return -1;
141 if (sscanf(version2, "%hhu.%hhu.%hhu",
142 &ver2[0], &ver2[1], &ver2[2]) != 3)
143 return -1;
144
145 for (i = 0; i < 3; i++) {
146 if (ver1[i] > ver2[i])
147 return 1;
148 if (ver1[i] < ver2[i])
Min Li7ea5fda2020-07-28 16:00:30 -0400149 return -1;
Min Li7ea5fda2020-07-28 16:00:30 -0400150 }
Min Li3cb2e6d2020-11-24 21:58:35 -0500151
152 return 0;
Min Li7ea5fda2020-07-28 16:00:30 -0400153}
154
Min Li957ff422020-08-18 10:41:22 -0400155static int idtcm_xfer_read(struct idtcm *idtcm,
156 u8 regaddr,
157 u8 *buf,
158 u16 count)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400159{
160 struct i2c_client *client = idtcm->client;
161 struct i2c_msg msg[2];
162 int cnt;
163
164 msg[0].addr = client->addr;
165 msg[0].flags = 0;
166 msg[0].len = 1;
167 msg[0].buf = &regaddr;
168
169 msg[1].addr = client->addr;
Min Li957ff422020-08-18 10:41:22 -0400170 msg[1].flags = I2C_M_RD;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400171 msg[1].len = count;
172 msg[1].buf = buf;
173
174 cnt = i2c_transfer(client->adapter, msg, 2);
175
176 if (cnt < 0) {
Min Li7ea5fda2020-07-28 16:00:30 -0400177 dev_err(&client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500178 "i2c_transfer failed at %d in %s, at addr: %04x!",
179 __LINE__, __func__, regaddr);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400180 return cnt;
181 } else if (cnt != 2) {
182 dev_err(&client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500183 "i2c_transfer sent only %d of %d messages", cnt, 2);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400184 return -EIO;
185 }
186
187 return 0;
188}
189
Min Li957ff422020-08-18 10:41:22 -0400190static int idtcm_xfer_write(struct idtcm *idtcm,
191 u8 regaddr,
192 u8 *buf,
193 u16 count)
194{
195 struct i2c_client *client = idtcm->client;
196 /* we add 1 byte for device register */
197 u8 msg[IDTCM_MAX_WRITE_COUNT + 1];
198 int cnt;
Min Li957ff422020-08-18 10:41:22 -0400199
200 if (count > IDTCM_MAX_WRITE_COUNT)
201 return -EINVAL;
202
203 msg[0] = regaddr;
204 memcpy(&msg[1], buf, count);
205
206 cnt = i2c_master_send(client, msg, count + 1);
207
208 if (cnt < 0) {
209 dev_err(&client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500210 "i2c_master_send failed at %d in %s, at addr: %04x!",
211 __LINE__, __func__, regaddr);
Min Li957ff422020-08-18 10:41:22 -0400212 return cnt;
213 }
214
215 return 0;
216}
217
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400218static int idtcm_page_offset(struct idtcm *idtcm, u8 val)
219{
220 u8 buf[4];
221 int err;
222
223 if (idtcm->page_offset == val)
224 return 0;
225
226 buf[0] = 0x0;
227 buf[1] = val;
228 buf[2] = 0x10;
229 buf[3] = 0x20;
230
Min Li957ff422020-08-18 10:41:22 -0400231 err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf));
Min Li7ea5fda2020-07-28 16:00:30 -0400232 if (err) {
233 idtcm->page_offset = 0xff;
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500234 dev_err(&idtcm->client->dev, "failed to set page offset");
Min Li7ea5fda2020-07-28 16:00:30 -0400235 } else {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400236 idtcm->page_offset = val;
Min Li7ea5fda2020-07-28 16:00:30 -0400237 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400238
239 return err;
240}
241
242static int _idtcm_rdwr(struct idtcm *idtcm,
243 u16 regaddr,
244 u8 *buf,
245 u16 count,
246 bool write)
247{
248 u8 hi;
249 u8 lo;
250 int err;
251
252 hi = (regaddr >> 8) & 0xff;
253 lo = regaddr & 0xff;
254
255 err = idtcm_page_offset(idtcm, hi);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400256 if (err)
Min Li957ff422020-08-18 10:41:22 -0400257 return err;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400258
Min Li957ff422020-08-18 10:41:22 -0400259 if (write)
260 return idtcm_xfer_write(idtcm, lo, buf, count);
261
262 return idtcm_xfer_read(idtcm, lo, buf, count);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400263}
264
265static int idtcm_read(struct idtcm *idtcm,
266 u16 module,
267 u16 regaddr,
268 u8 *buf,
269 u16 count)
270{
271 return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false);
272}
273
274static int idtcm_write(struct idtcm *idtcm,
275 u16 module,
276 u16 regaddr,
277 u8 *buf,
278 u16 count)
279{
280 return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
281}
282
Min Li251f4fe2020-12-08 10:41:54 -0500283static int clear_boot_status(struct idtcm *idtcm)
284{
285 int err;
286 u8 buf[4] = {0};
287
288 err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
289
290 return err;
291}
292
293static int read_boot_status(struct idtcm *idtcm, u32 *status)
294{
295 int err;
296 u8 buf[4] = {0};
297
298 err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
299
300 *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
301
302 return err;
303}
304
305static int wait_for_boot_status_ready(struct idtcm *idtcm)
306{
307 u32 status = 0;
308 u8 i = 30; /* 30 * 100ms = 3s */
309 int err;
310
311 do {
312 err = read_boot_status(idtcm, &status);
Min Li251f4fe2020-12-08 10:41:54 -0500313 if (err)
314 return err;
315
316 if (status == 0xA0)
317 return 0;
318
319 msleep(100);
320 i--;
321
322 } while (i);
323
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500324 dev_warn(&idtcm->client->dev, "%s timed out", __func__);
Min Li251f4fe2020-12-08 10:41:54 -0500325
326 return -EBUSY;
327}
328
Vincent Cheng797d3182021-02-17 00:42:12 -0500329static int read_sys_apll_status(struct idtcm *idtcm, u8 *status)
330{
331 return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status,
332 sizeof(u8));
333}
334
335static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status)
336{
337 return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8));
338}
339
340static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm)
341{
342 unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS);
343 u8 apll = 0;
344 u8 dpll = 0;
345 int err;
346
347 do {
348 err = read_sys_apll_status(idtcm, &apll);
349 if (err)
350 return err;
351
352 err = read_sys_dpll_status(idtcm, &dpll);
353 if (err)
354 return err;
355
356 apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK;
357 dpll &= DPLL_SYS_STATE_MASK;
358
359 if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED &&
360 dpll == DPLL_STATE_LOCKED) {
361 return 0;
362 } else if (dpll == DPLL_STATE_FREERUN ||
363 dpll == DPLL_STATE_HOLDOVER ||
364 dpll == DPLL_STATE_OPEN_LOOP) {
365 dev_warn(&idtcm->client->dev,
366 "No wait state: DPLL_SYS_STATE %d", dpll);
367 return -EPERM;
368 }
369
370 msleep(LOCK_POLL_INTERVAL_MS);
371 } while (time_is_after_jiffies(timeout));
372
373 dev_warn(&idtcm->client->dev,
374 "%d ms lock timeout: SYS APLL Loss Lock %d SYS DPLL state %d",
375 LOCK_TIMEOUT_MS, apll, dpll);
376
377 return -ETIME;
378}
379
380static void wait_for_chip_ready(struct idtcm *idtcm)
381{
382 if (wait_for_boot_status_ready(idtcm))
383 dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0");
384
385 if (wait_for_sys_apll_dpll_lock(idtcm))
386 dev_warn(&idtcm->client->dev,
387 "Continuing while SYS APLL/DPLL is not locked");
388}
389
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400390static int _idtcm_gettime(struct idtcm_channel *channel,
391 struct timespec64 *ts)
392{
393 struct idtcm *idtcm = channel->idtcm;
394 u8 buf[TOD_BYTE_COUNT];
Min Li7ea5fda2020-07-28 16:00:30 -0400395 u8 timeout = 10;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400396 u8 trigger;
397 int err;
398
399 err = idtcm_read(idtcm, channel->tod_read_primary,
400 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
401 if (err)
402 return err;
403
404 trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
405 trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
Min Li7ea5fda2020-07-28 16:00:30 -0400406 trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400407
408 err = idtcm_write(idtcm, channel->tod_read_primary,
409 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400410 if (err)
411 return err;
412
Min Li7ea5fda2020-07-28 16:00:30 -0400413 /* wait trigger to be 0 */
414 while (trigger & TOD_READ_TRIGGER_MASK) {
Min Li7ea5fda2020-07-28 16:00:30 -0400415 if (idtcm->calculate_overhead_flag)
416 idtcm->start_time = ktime_get_raw();
417
418 err = idtcm_read(idtcm, channel->tod_read_primary,
419 TOD_READ_PRIMARY_CMD, &trigger,
420 sizeof(trigger));
Min Li7ea5fda2020-07-28 16:00:30 -0400421 if (err)
422 return err;
423
424 if (--timeout == 0)
425 return -EIO;
426 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400427
428 err = idtcm_read(idtcm, channel->tod_read_primary,
429 TOD_READ_PRIMARY, buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400430 if (err)
431 return err;
432
433 err = char_array_to_timespec(buf, sizeof(buf), ts);
434
435 return err;
436}
437
438static int _sync_pll_output(struct idtcm *idtcm,
439 u8 pll,
440 u8 sync_src,
441 u8 qn,
442 u8 qn_plus_1)
443{
444 int err;
445 u8 val;
446 u16 sync_ctrl0;
447 u16 sync_ctrl1;
Min Li7ea5fda2020-07-28 16:00:30 -0400448 u8 temp;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400449
450 if ((qn == 0) && (qn_plus_1 == 0))
451 return 0;
452
453 switch (pll) {
454 case 0:
455 sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
456 sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
457 break;
458 case 1:
459 sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
460 sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
461 break;
462 case 2:
463 sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
464 sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
465 break;
466 case 3:
467 sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
468 sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
469 break;
470 case 4:
471 sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
472 sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
473 break;
474 case 5:
475 sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
476 sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
477 break;
478 case 6:
479 sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
480 sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
481 break;
482 case 7:
483 sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
484 sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
485 break;
486 default:
487 return -EINVAL;
488 }
489
490 val = SYNCTRL1_MASTER_SYNC_RST;
491
492 /* Place master sync in reset */
493 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
494 if (err)
495 return err;
496
497 err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
498 if (err)
499 return err;
500
501 /* Set sync trigger mask */
502 val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
503
504 if (qn)
505 val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
506
507 if (qn_plus_1)
508 val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
509
510 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
511 if (err)
512 return err;
513
Min Li7ea5fda2020-07-28 16:00:30 -0400514 /* PLL5 can have OUT8 as second additional output. */
515 if ((pll == 5) && (qn_plus_1 != 0)) {
516 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
517 &temp, sizeof(temp));
518 if (err)
519 return err;
520
521 temp &= ~(Q9_TO_Q8_SYNC_TRIG);
522
523 err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
524 &temp, sizeof(temp));
525 if (err)
526 return err;
527
528 temp |= Q9_TO_Q8_SYNC_TRIG;
529
530 err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
531 &temp, sizeof(temp));
532 if (err)
533 return err;
534 }
535
536 /* PLL6 can have OUT11 as second additional output. */
537 if ((pll == 6) && (qn_plus_1 != 0)) {
538 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
539 &temp, sizeof(temp));
540 if (err)
541 return err;
542
543 temp &= ~(Q10_TO_Q11_SYNC_TRIG);
544
545 err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
546 &temp, sizeof(temp));
547 if (err)
548 return err;
549
550 temp |= Q10_TO_Q11_SYNC_TRIG;
551
552 err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
553 &temp, sizeof(temp));
554 if (err)
555 return err;
556 }
557
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400558 /* Place master sync out of reset */
559 val &= ~(SYNCTRL1_MASTER_SYNC_RST);
560 err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
561
562 return err;
563}
564
Min Li7ea5fda2020-07-28 16:00:30 -0400565static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
566{
567 int err = 0;
568
569 switch (tod_addr) {
570 case TOD_0:
571 *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
572 break;
573 case TOD_1:
574 *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
575 break;
576 case TOD_2:
577 *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
578 break;
579 case TOD_3:
580 *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
581 break;
582 default:
583 err = -EINVAL;
584 }
585
586 return err;
587}
588
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400589static int idtcm_sync_pps_output(struct idtcm_channel *channel)
590{
591 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400592 u8 pll;
593 u8 sync_src;
594 u8 qn;
595 u8 qn_plus_1;
596 int err = 0;
Min Li7ea5fda2020-07-28 16:00:30 -0400597 u8 out8_mux = 0;
598 u8 out11_mux = 0;
599 u8 temp;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400600 u16 output_mask = channel->output_mask;
601
Min Li7ea5fda2020-07-28 16:00:30 -0400602 err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
603 if (err)
604 return err;
605
606 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
607 &temp, sizeof(temp));
608 if (err)
609 return err;
610
611 if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
612 Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
613 out8_mux = 1;
614
615 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
616 &temp, sizeof(temp));
617 if (err)
618 return err;
619
620 if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
621 Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
622 out11_mux = 1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400623
624 for (pll = 0; pll < 8; pll++) {
Min Li7ea5fda2020-07-28 16:00:30 -0400625 qn = 0;
626 qn_plus_1 = 0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400627
628 if (pll < 4) {
629 /* First 4 pll has 2 outputs */
Min Li7ea5fda2020-07-28 16:00:30 -0400630 qn = output_mask & 0x1;
631 output_mask = output_mask >> 1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400632 qn_plus_1 = output_mask & 0x1;
633 output_mask = output_mask >> 1;
Min Li7ea5fda2020-07-28 16:00:30 -0400634 } else if (pll == 4) {
635 if (out8_mux == 0) {
636 qn = output_mask & 0x1;
637 output_mask = output_mask >> 1;
638 }
639 } else if (pll == 5) {
640 if (out8_mux) {
641 qn_plus_1 = output_mask & 0x1;
642 output_mask = output_mask >> 1;
643 }
644 qn = output_mask & 0x1;
645 output_mask = output_mask >> 1;
646 } else if (pll == 6) {
647 qn = output_mask & 0x1;
648 output_mask = output_mask >> 1;
649 if (out11_mux) {
650 qn_plus_1 = output_mask & 0x1;
651 output_mask = output_mask >> 1;
652 }
653 } else if (pll == 7) {
654 if (out11_mux == 0) {
655 qn = output_mask & 0x1;
656 output_mask = output_mask >> 1;
657 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400658 }
659
660 if ((qn != 0) || (qn_plus_1 != 0))
661 err = _sync_pll_output(idtcm, pll, sync_src, qn,
662 qn_plus_1);
663
664 if (err)
665 return err;
666 }
667
668 return err;
669}
670
Min Li7ea5fda2020-07-28 16:00:30 -0400671static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400672 struct timespec64 const *ts,
673 enum hw_tod_write_trig_sel wr_trig)
674{
675 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400676 u8 buf[TOD_BYTE_COUNT];
677 u8 cmd;
678 int err;
679 struct timespec64 local_ts = *ts;
680 s64 total_overhead_ns;
681
682 /* Configure HW TOD write trigger. */
683 err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
684 &cmd, sizeof(cmd));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400685 if (err)
686 return err;
687
688 cmd &= ~(0x0f);
689 cmd |= wr_trig | 0x08;
690
691 err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
692 &cmd, sizeof(cmd));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400693 if (err)
694 return err;
695
696 if (wr_trig != HW_TOD_WR_TRIG_SEL_MSB) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400697 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400698 if (err)
699 return err;
700
701 err = idtcm_write(idtcm, channel->hw_dpll_n,
702 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400703 if (err)
704 return err;
705 }
706
707 /* ARM HW TOD write trigger. */
708 cmd &= ~(0x08);
709
710 err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
711 &cmd, sizeof(cmd));
712
713 if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400714 if (idtcm->calculate_overhead_flag) {
Vincent Cheng1ece2fb2020-01-07 09:47:57 -0500715 /* Assumption: I2C @ 400KHz */
Min Li7260d1c2020-12-08 10:41:56 -0500716 ktime_t diff = ktime_sub(ktime_get_raw(),
717 idtcm->start_time);
718 total_overhead_ns = ktime_to_ns(diff)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400719 + idtcm->tod_write_overhead_ns
720 + SETTIME_CORRECTION;
721
722 timespec64_add_ns(&local_ts, total_overhead_ns);
723
724 idtcm->calculate_overhead_flag = 0;
725 }
726
727 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400728 if (err)
729 return err;
730
731 err = idtcm_write(idtcm, channel->hw_dpll_n,
732 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
733 }
734
735 return err;
736}
737
Min Li7ea5fda2020-07-28 16:00:30 -0400738static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
739 struct timespec64 const *ts,
740 enum scsr_tod_write_trig_sel wr_trig,
741 enum scsr_tod_write_type_sel wr_type)
742{
743 struct idtcm *idtcm = channel->idtcm;
744 unsigned char buf[TOD_BYTE_COUNT], cmd;
745 struct timespec64 local_ts = *ts;
746 int err, count = 0;
747
748 timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
749
750 err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
Min Li7ea5fda2020-07-28 16:00:30 -0400751 if (err)
752 return err;
753
754 err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
755 buf, sizeof(buf));
756 if (err)
757 return err;
758
759 /* Trigger the write operation. */
760 err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
761 &cmd, sizeof(cmd));
762 if (err)
763 return err;
764
765 cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
766 cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
767 cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
768 cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
769
770 err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
771 &cmd, sizeof(cmd));
772 if (err)
773 return err;
774
775 /* Wait for the operation to complete. */
776 while (1) {
777 /* pps trigger takes up to 1 sec to complete */
778 if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
779 msleep(50);
780
781 err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
782 &cmd, sizeof(cmd));
783 if (err)
784 return err;
785
Min Li251f4fe2020-12-08 10:41:54 -0500786 if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
Min Li7ea5fda2020-07-28 16:00:30 -0400787 break;
788
789 if (++count > 20) {
790 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500791 "Timed out waiting for the write counter");
Min Li7ea5fda2020-07-28 16:00:30 -0400792 return -EIO;
793 }
794 }
795
796 return 0;
797}
798
Min Li7260d1c2020-12-08 10:41:56 -0500799static int get_output_base_addr(u8 outn)
800{
801 int base;
802
803 switch (outn) {
804 case 0:
805 base = OUTPUT_0;
806 break;
807 case 1:
808 base = OUTPUT_1;
809 break;
810 case 2:
811 base = OUTPUT_2;
812 break;
813 case 3:
814 base = OUTPUT_3;
815 break;
816 case 4:
817 base = OUTPUT_4;
818 break;
819 case 5:
820 base = OUTPUT_5;
821 break;
822 case 6:
823 base = OUTPUT_6;
824 break;
825 case 7:
826 base = OUTPUT_7;
827 break;
828 case 8:
829 base = OUTPUT_8;
830 break;
831 case 9:
832 base = OUTPUT_9;
833 break;
834 case 10:
835 base = OUTPUT_10;
836 break;
837 case 11:
838 base = OUTPUT_11;
839 break;
840 default:
841 base = -EINVAL;
842 }
843
844 return base;
845}
846
Min Lida948232020-12-08 10:41:57 -0500847static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
848 struct timespec64 const *ts)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400849{
850 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400851 int err;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400852
Min Li251f4fe2020-12-08 10:41:54 -0500853 err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
Min Li7ea5fda2020-07-28 16:00:30 -0400854 if (err) {
855 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -0500856 "%s: Set HW ToD failed", __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400857 return err;
Min Li7ea5fda2020-07-28 16:00:30 -0400858 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400859
Min Li7ea5fda2020-07-28 16:00:30 -0400860 return idtcm_sync_pps_output(channel);
861}
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400862
Min Lida948232020-12-08 10:41:57 -0500863static int _idtcm_settime(struct idtcm_channel *channel,
864 struct timespec64 const *ts,
865 enum scsr_tod_write_type_sel wr_type)
Min Li7ea5fda2020-07-28 16:00:30 -0400866{
867 return _idtcm_set_dpll_scsr_tod(channel, ts,
868 SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
869 wr_type);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400870}
871
872static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
873 s32 offset_ns)
874{
875 int err;
876 int i;
877 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400878 u8 buf[4];
879
880 for (i = 0; i < 4; i++) {
881 buf[i] = 0xff & (offset_ns);
882 offset_ns >>= 8;
883 }
884
885 err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
886 buf, sizeof(buf));
887
888 return err;
889}
890
891static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
892 u32 max_ffo_ppb)
893{
894 int err;
895 u8 i;
896 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400897 u8 buf[3];
898
899 if (max_ffo_ppb & 0xff000000)
900 max_ffo_ppb = 0;
901
902 for (i = 0; i < 3; i++) {
903 buf[i] = 0xff & (max_ffo_ppb);
904 max_ffo_ppb >>= 8;
905 }
906
907 err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
908 PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
909
910 return err;
911}
912
913static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
914{
915 int err;
916 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400917 u8 buf;
918
919 err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
920 &buf, sizeof(buf));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400921 if (err)
922 return err;
923
924 if (buf == 0) {
925 buf = 0x01;
926 err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
927 PULL_IN_CTRL, &buf, sizeof(buf));
928 } else {
929 err = -EBUSY;
930 }
931
932 return err;
933}
934
935static int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
936 s32 offset_ns,
937 u32 max_ffo_ppb)
938{
939 int err;
940
941 err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400942 if (err)
943 return err;
944
945 err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400946 if (err)
947 return err;
948
949 err = idtcm_start_phase_pull_in(channel);
950
951 return err;
952}
953
Min Li7ea5fda2020-07-28 16:00:30 -0400954static int set_tod_write_overhead(struct idtcm_channel *channel)
955{
956 struct idtcm *idtcm = channel->idtcm;
957 s64 current_ns = 0;
958 s64 lowest_ns = 0;
959 int err;
960 u8 i;
Min Li7ea5fda2020-07-28 16:00:30 -0400961 ktime_t start;
962 ktime_t stop;
Min Li7260d1c2020-12-08 10:41:56 -0500963 ktime_t diff;
Min Li7ea5fda2020-07-28 16:00:30 -0400964
965 char buf[TOD_BYTE_COUNT] = {0};
966
967 /* Set page offset */
968 idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
969 buf, sizeof(buf));
970
971 for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
Min Li7ea5fda2020-07-28 16:00:30 -0400972 start = ktime_get_raw();
973
974 err = idtcm_write(idtcm, channel->hw_dpll_n,
975 HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
Min Li7ea5fda2020-07-28 16:00:30 -0400976 if (err)
977 return err;
978
979 stop = ktime_get_raw();
980
Min Li7260d1c2020-12-08 10:41:56 -0500981 diff = ktime_sub(stop, start);
982
983 current_ns = ktime_to_ns(diff);
Min Li7ea5fda2020-07-28 16:00:30 -0400984
985 if (i == 0) {
986 lowest_ns = current_ns;
987 } else {
988 if (current_ns < lowest_ns)
989 lowest_ns = current_ns;
990 }
991 }
992
993 idtcm->tod_write_overhead_ns = lowest_ns;
994
995 return err;
996}
997
Min Lida948232020-12-08 10:41:57 -0500998static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -0400999{
1000 int err;
1001 struct idtcm *idtcm = channel->idtcm;
1002 struct timespec64 ts;
1003 s64 now;
1004
Min Lida948232020-12-08 10:41:57 -05001005 if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001006 err = idtcm_do_phase_pull_in(channel, delta, 0);
1007 } else {
1008 idtcm->calculate_overhead_flag = 1;
1009
Min Li7ea5fda2020-07-28 16:00:30 -04001010 err = set_tod_write_overhead(channel);
Min Li7ea5fda2020-07-28 16:00:30 -04001011 if (err)
1012 return err;
1013
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001014 err = _idtcm_gettime(channel, &ts);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001015 if (err)
1016 return err;
1017
1018 now = timespec64_to_ns(&ts);
1019 now += delta;
1020
1021 ts = ns_to_timespec64(now);
1022
Min Lida948232020-12-08 10:41:57 -05001023 err = _idtcm_settime_deprecated(channel, &ts);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001024 }
1025
1026 return err;
1027}
1028
1029static int idtcm_state_machine_reset(struct idtcm *idtcm)
1030{
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001031 u8 byte = SM_RESET_CMD;
Min Li251f4fe2020-12-08 10:41:54 -05001032 u32 status = 0;
1033 int err;
1034 u8 i;
1035
1036 clear_boot_status(idtcm);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001037
1038 err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
1039
Min Li251f4fe2020-12-08 10:41:54 -05001040 if (!err) {
1041 for (i = 0; i < 30; i++) {
1042 msleep_interruptible(100);
1043 read_boot_status(idtcm, &status);
1044
1045 if (status == 0xA0) {
1046 dev_dbg(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001047 "SM_RESET completed in %d ms", i * 100);
Min Li251f4fe2020-12-08 10:41:54 -05001048 break;
1049 }
1050 }
1051
1052 if (!status)
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001053 dev_err(&idtcm->client->dev,
1054 "Timed out waiting for CM_RESET to complete");
Min Li251f4fe2020-12-08 10:41:54 -05001055 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001056
1057 return err;
1058}
1059
1060static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
1061{
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001062 return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001063}
1064
1065static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
1066{
1067 int err;
1068 u8 buf[2] = {0};
1069
1070 err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
1071
1072 *product_id = (buf[1] << 8) | buf[0];
1073
1074 return err;
1075}
1076
1077static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
1078{
1079 int err;
1080 u8 buf = 0;
1081
1082 err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
1083
1084 *major = buf >> 1;
1085
1086 return err;
1087}
1088
1089static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
1090{
1091 return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
1092}
1093
1094static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
1095{
1096 return idtcm_read(idtcm,
1097 GENERAL_STATUS,
1098 HOTFIX_REL,
1099 hotfix,
1100 sizeof(u8));
1101}
1102
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001103static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
1104 u8 *config_select)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001105{
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001106 return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
1107 config_select, sizeof(u8));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001108}
1109
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001110static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
1111{
1112 int err = 0;
1113
1114 switch (addr) {
Min Li7ea5fda2020-07-28 16:00:30 -04001115 case TOD0_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001116 SET_U16_LSB(idtcm->channel[0].output_mask, val);
1117 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001118 case TOD0_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001119 SET_U16_MSB(idtcm->channel[0].output_mask, val);
1120 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001121 case TOD1_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001122 SET_U16_LSB(idtcm->channel[1].output_mask, val);
1123 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001124 case TOD1_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001125 SET_U16_MSB(idtcm->channel[1].output_mask, val);
1126 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001127 case TOD2_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001128 SET_U16_LSB(idtcm->channel[2].output_mask, val);
1129 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001130 case TOD2_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001131 SET_U16_MSB(idtcm->channel[2].output_mask, val);
1132 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001133 case TOD3_OUT_ALIGN_MASK_ADDR:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001134 SET_U16_LSB(idtcm->channel[3].output_mask, val);
1135 break;
Min Li7ea5fda2020-07-28 16:00:30 -04001136 case TOD3_OUT_ALIGN_MASK_ADDR + 1:
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001137 SET_U16_MSB(idtcm->channel[3].output_mask, val);
1138 break;
1139 default:
Min Li7ea5fda2020-07-28 16:00:30 -04001140 err = -EFAULT; /* Bad address */;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001141 break;
1142 }
1143
1144 return err;
1145}
1146
Min Li7ea5fda2020-07-28 16:00:30 -04001147static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
1148{
1149 if (index >= MAX_TOD) {
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001150 dev_err(&idtcm->client->dev, "ToD%d not supported", index);
Min Li7ea5fda2020-07-28 16:00:30 -04001151 return -EINVAL;
1152 }
1153
1154 if (pll >= MAX_PLL) {
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001155 dev_err(&idtcm->client->dev, "Pll%d not supported", pll);
Min Li7ea5fda2020-07-28 16:00:30 -04001156 return -EINVAL;
1157 }
1158
1159 idtcm->channel[index].pll = pll;
1160
1161 return 0;
1162}
1163
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001164static int check_and_set_masks(struct idtcm *idtcm,
1165 u16 regaddr,
1166 u8 val)
1167{
1168 int err = 0;
1169
Min Li7ea5fda2020-07-28 16:00:30 -04001170 switch (regaddr) {
1171 case TOD_MASK_ADDR:
1172 if ((val & 0xf0) || !(val & 0x0f)) {
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001173 dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val);
Min Li7ea5fda2020-07-28 16:00:30 -04001174 err = -EINVAL;
1175 } else {
1176 idtcm->tod_mask = val;
1177 }
1178 break;
1179 case TOD0_PTP_PLL_ADDR:
1180 err = set_tod_ptp_pll(idtcm, 0, val);
1181 break;
1182 case TOD1_PTP_PLL_ADDR:
1183 err = set_tod_ptp_pll(idtcm, 1, val);
1184 break;
1185 case TOD2_PTP_PLL_ADDR:
1186 err = set_tod_ptp_pll(idtcm, 2, val);
1187 break;
1188 case TOD3_PTP_PLL_ADDR:
1189 err = set_tod_ptp_pll(idtcm, 3, val);
1190 break;
1191 default:
1192 err = set_pll_output_mask(idtcm, regaddr, val);
1193 break;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001194 }
1195
1196 return err;
1197}
1198
Min Li7ea5fda2020-07-28 16:00:30 -04001199static void display_pll_and_masks(struct idtcm *idtcm)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001200{
1201 u8 i;
1202 u8 mask;
1203
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001204 dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001205
Min Li7ea5fda2020-07-28 16:00:30 -04001206 for (i = 0; i < MAX_TOD; i++) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001207 mask = 1 << i;
1208
Min Li7ea5fda2020-07-28 16:00:30 -04001209 if (mask & idtcm->tod_mask)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001210 dev_dbg(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001211 "TOD%d pll = %d output_mask = 0x%04x",
Min Li7ea5fda2020-07-28 16:00:30 -04001212 i, idtcm->channel[i].pll,
1213 idtcm->channel[i].output_mask);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001214 }
1215}
1216
1217static int idtcm_load_firmware(struct idtcm *idtcm,
1218 struct device *dev)
1219{
Min Li7ea5fda2020-07-28 16:00:30 -04001220 char fname[128] = FW_FILENAME;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001221 const struct firmware *fw;
1222 struct idtcm_fwrc *rec;
1223 u32 regaddr;
1224 int err;
1225 s32 len;
1226 u8 val;
1227 u8 loaddr;
1228
Min Li7ea5fda2020-07-28 16:00:30 -04001229 if (firmware) /* module parameter */
1230 snprintf(fname, sizeof(fname), "%s", firmware);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001231
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001232 dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001233
Min Li7ea5fda2020-07-28 16:00:30 -04001234 err = request_firmware(&fw, fname, dev);
Min Li7ea5fda2020-07-28 16:00:30 -04001235 if (err) {
1236 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001237 "Failed at line %d in %s!", __LINE__, __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001238 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04001239 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001240
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001241 dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001242
1243 rec = (struct idtcm_fwrc *) fw->data;
1244
Min Li251f4fe2020-12-08 10:41:54 -05001245 if (contains_full_configuration(fw))
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001246 idtcm_state_machine_reset(idtcm);
1247
1248 for (len = fw->size; len > 0; len -= sizeof(*rec)) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001249 if (rec->reserved) {
1250 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001251 "bad firmware, reserved field non-zero");
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001252 err = -EINVAL;
1253 } else {
1254 regaddr = rec->hiaddr << 8;
1255 regaddr |= rec->loaddr;
1256
1257 val = rec->value;
1258 loaddr = rec->loaddr;
1259
1260 rec++;
1261
1262 err = check_and_set_masks(idtcm, regaddr, val);
1263 }
1264
Min Li7ea5fda2020-07-28 16:00:30 -04001265 if (err != -EINVAL) {
1266 err = 0;
1267
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001268 /* Top (status registers) and bottom are read-only */
1269 if ((regaddr < GPIO_USER_CONTROL)
1270 || (regaddr >= SCRATCH))
1271 continue;
1272
1273 /* Page size 128, last 4 bytes of page skipped */
1274 if (((loaddr > 0x7b) && (loaddr <= 0x7f))
Yang Yingliang3e144622020-04-24 20:52:26 +08001275 || loaddr > 0xfb)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001276 continue;
1277
1278 err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
1279 }
1280
1281 if (err)
1282 goto out;
1283 }
1284
Min Li7ea5fda2020-07-28 16:00:30 -04001285 display_pll_and_masks(idtcm);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001286
1287out:
1288 release_firmware(fw);
1289 return err;
1290}
1291
Min Li7ea5fda2020-07-28 16:00:30 -04001292static int idtcm_output_enable(struct idtcm_channel *channel,
1293 bool enable, unsigned int outn)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001294{
1295 struct idtcm *idtcm = channel->idtcm;
Min Li7260d1c2020-12-08 10:41:56 -05001296 int base;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001297 int err;
Min Li7ea5fda2020-07-28 16:00:30 -04001298 u8 val;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001299
Min Li7260d1c2020-12-08 10:41:56 -05001300 base = get_output_base_addr(outn);
1301
1302 if (!(base > 0)) {
1303 dev_err(&idtcm->client->dev,
1304 "%s - Unsupported out%d", __func__, outn);
1305 return base;
1306 }
1307
1308 err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001309 if (err)
1310 return err;
1311
1312 if (enable)
1313 val |= SQUELCH_DISABLE;
1314 else
1315 val &= ~SQUELCH_DISABLE;
1316
Min Li7260d1c2020-12-08 10:41:56 -05001317 return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
Min Li7ea5fda2020-07-28 16:00:30 -04001318}
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001319
Min Li7ea5fda2020-07-28 16:00:30 -04001320static int idtcm_output_mask_enable(struct idtcm_channel *channel,
1321 bool enable)
1322{
1323 u16 mask;
1324 int err;
1325 u8 outn;
1326
1327 mask = channel->output_mask;
1328 outn = 0;
1329
1330 while (mask) {
Min Li7ea5fda2020-07-28 16:00:30 -04001331 if (mask & 0x1) {
Min Li7ea5fda2020-07-28 16:00:30 -04001332 err = idtcm_output_enable(channel, enable, outn);
Min Li7ea5fda2020-07-28 16:00:30 -04001333 if (err)
1334 return err;
1335 }
1336
1337 mask >>= 0x1;
1338 outn++;
1339 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001340
1341 return 0;
1342}
1343
Min Li7ea5fda2020-07-28 16:00:30 -04001344static int idtcm_perout_enable(struct idtcm_channel *channel,
1345 bool enable,
1346 struct ptp_perout_request *perout)
1347{
Vincent Chenge8b4d8b2021-02-17 00:42:13 -05001348 struct idtcm *idtcm = channel->idtcm;
Min Li7ea5fda2020-07-28 16:00:30 -04001349 unsigned int flags = perout->flags;
Vincent Chenge8b4d8b2021-02-17 00:42:13 -05001350 struct timespec64 ts = {0, 0};
1351 int err;
Min Li7ea5fda2020-07-28 16:00:30 -04001352
1353 if (flags == PEROUT_ENABLE_OUTPUT_MASK)
Vincent Chenge8b4d8b2021-02-17 00:42:13 -05001354 err = idtcm_output_mask_enable(channel, enable);
1355 else
1356 err = idtcm_output_enable(channel, enable, perout->index);
Min Li7ea5fda2020-07-28 16:00:30 -04001357
Vincent Chenge8b4d8b2021-02-17 00:42:13 -05001358 if (err) {
1359 dev_err(&idtcm->client->dev, "Unable to set output enable");
1360 return err;
1361 }
1362
1363 /* Align output to internal 1 PPS */
1364 return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS);
Min Li7ea5fda2020-07-28 16:00:30 -04001365}
1366
Min Li7260d1c2020-12-08 10:41:56 -05001367static int idtcm_get_pll_mode(struct idtcm_channel *channel,
1368 enum pll_mode *pll_mode)
1369{
1370 struct idtcm *idtcm = channel->idtcm;
1371 int err;
1372 u8 dpll_mode;
1373
1374 err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
1375 &dpll_mode, sizeof(dpll_mode));
1376 if (err)
1377 return err;
1378
1379 *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
1380
1381 return 0;
1382}
1383
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001384static int idtcm_set_pll_mode(struct idtcm_channel *channel,
1385 enum pll_mode pll_mode)
1386{
1387 struct idtcm *idtcm = channel->idtcm;
1388 int err;
1389 u8 dpll_mode;
1390
1391 err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
1392 &dpll_mode, sizeof(dpll_mode));
1393 if (err)
1394 return err;
1395
1396 dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
1397
1398 dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
1399
1400 channel->pll_mode = pll_mode;
1401
1402 err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
1403 &dpll_mode, sizeof(dpll_mode));
1404 if (err)
1405 return err;
1406
1407 return 0;
1408}
1409
1410/* PTP Hardware Clock interface */
1411
Vincent Cheng425d2b12020-05-01 23:35:38 -04001412/**
1413 * @brief Maximum absolute value for write phase offset in picoseconds
1414 *
1415 * Destination signed register is 32-bit register in resolution of 50ps
1416 *
1417 * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
1418 */
1419static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1420{
1421 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng425d2b12020-05-01 23:35:38 -04001422 int err;
1423 u8 i;
1424 u8 buf[4] = {0};
1425 s32 phase_50ps;
1426 s64 offset_ps;
1427
1428 if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
Vincent Cheng425d2b12020-05-01 23:35:38 -04001429 err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
Vincent Cheng425d2b12020-05-01 23:35:38 -04001430 if (err)
1431 return err;
Vincent Cheng425d2b12020-05-01 23:35:38 -04001432 }
1433
Vincent Cheng425d2b12020-05-01 23:35:38 -04001434 offset_ps = (s64)delta_ns * 1000;
1435
1436 /*
1437 * Check for 32-bit signed max * 50:
1438 *
1439 * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350
1440 */
1441 if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
1442 offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
1443 else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
1444 offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
1445
Min Li7260d1c2020-12-08 10:41:56 -05001446 phase_50ps = div_s64(offset_ps, 50);
Vincent Cheng425d2b12020-05-01 23:35:38 -04001447
1448 for (i = 0; i < 4; i++) {
1449 buf[i] = phase_50ps & 0xff;
1450 phase_50ps >>= 8;
1451 }
1452
1453 err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1454 buf, sizeof(buf));
1455
1456 return err;
1457}
1458
Min Li7ea5fda2020-07-28 16:00:30 -04001459static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001460{
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001461 struct idtcm *idtcm = channel->idtcm;
1462 u8 i;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001463 int err;
1464 u8 buf[6] = {0};
1465 s64 fcw;
1466
1467 if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) {
1468 err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
1469 if (err)
1470 return err;
1471 }
1472
1473 /*
1474 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
1475 *
1476 * adjfreq:
1477 * ppb * 10^9
1478 * FCW = ----------
1479 * 111
1480 *
1481 * adjfine:
1482 * ppm_16 * 5^12
1483 * FCW = -------------
1484 * 111 * 2^4
1485 */
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001486
1487 /* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
Min Li7ea5fda2020-07-28 16:00:30 -04001488 fcw = scaled_ppm * 244140625ULL;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001489
Min Li7260d1c2020-12-08 10:41:56 -05001490 fcw = div_s64(fcw, 1776);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001491
1492 for (i = 0; i < 6; i++) {
1493 buf[i] = fcw & 0xff;
1494 fcw >>= 8;
1495 }
1496
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001497 err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
1498 buf, sizeof(buf));
1499
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001500 return err;
1501}
1502
1503static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
1504{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001505 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001506 struct idtcm *idtcm = channel->idtcm;
1507 int err;
1508
1509 mutex_lock(&idtcm->reg_lock);
1510
1511 err = _idtcm_gettime(channel, ts);
Min Li7ea5fda2020-07-28 16:00:30 -04001512 if (err)
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001513 dev_err(&idtcm->client->dev, "Failed at line %d in %s!",
1514 __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001515
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001516 mutex_unlock(&idtcm->reg_lock);
1517
1518 return err;
1519}
1520
Min Lida948232020-12-08 10:41:57 -05001521static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
1522 const struct timespec64 *ts)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001523{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001524 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001525 struct idtcm *idtcm = channel->idtcm;
1526 int err;
1527
1528 mutex_lock(&idtcm->reg_lock);
1529
Min Lida948232020-12-08 10:41:57 -05001530 err = _idtcm_settime_deprecated(channel, ts);
Min Li7ea5fda2020-07-28 16:00:30 -04001531 if (err)
1532 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001533 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001534
1535 mutex_unlock(&idtcm->reg_lock);
1536
1537 return err;
1538}
1539
Min Lida948232020-12-08 10:41:57 -05001540static int idtcm_settime(struct ptp_clock_info *ptp,
Min Li7ea5fda2020-07-28 16:00:30 -04001541 const struct timespec64 *ts)
1542{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001543 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Min Li7ea5fda2020-07-28 16:00:30 -04001544 struct idtcm *idtcm = channel->idtcm;
1545 int err;
1546
1547 mutex_lock(&idtcm->reg_lock);
1548
Min Lida948232020-12-08 10:41:57 -05001549 err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
Min Lida948232020-12-08 10:41:57 -05001550 if (err)
1551 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001552 "Failed at line %d in %s!", __LINE__, __func__);
Min Lida948232020-12-08 10:41:57 -05001553
1554 mutex_unlock(&idtcm->reg_lock);
1555
1556 return err;
1557}
1558
1559static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
1560{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001561 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Min Lida948232020-12-08 10:41:57 -05001562 struct idtcm *idtcm = channel->idtcm;
1563 int err;
1564
1565 mutex_lock(&idtcm->reg_lock);
1566
1567 err = _idtcm_adjtime_deprecated(channel, delta);
Min Li7ea5fda2020-07-28 16:00:30 -04001568 if (err)
1569 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001570 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001571
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001572 mutex_unlock(&idtcm->reg_lock);
1573
1574 return err;
1575}
1576
1577static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
1578{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001579 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001580 struct idtcm *idtcm = channel->idtcm;
Min Li7ea5fda2020-07-28 16:00:30 -04001581 struct timespec64 ts;
1582 enum scsr_tod_write_type_sel type;
1583 int err;
1584
Min Lida948232020-12-08 10:41:57 -05001585 if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
Min Li7ea5fda2020-07-28 16:00:30 -04001586 err = idtcm_do_phase_pull_in(channel, delta, 0);
1587 if (err)
1588 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001589 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001590 return err;
1591 }
1592
1593 if (delta >= 0) {
1594 ts = ns_to_timespec64(delta);
1595 type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
1596 } else {
1597 ts = ns_to_timespec64(-delta);
1598 type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
1599 }
1600
1601 mutex_lock(&idtcm->reg_lock);
1602
Min Lida948232020-12-08 10:41:57 -05001603 err = _idtcm_settime(channel, &ts, type);
Min Li7ea5fda2020-07-28 16:00:30 -04001604 if (err)
1605 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001606 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001607
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001608 mutex_unlock(&idtcm->reg_lock);
1609
1610 return err;
1611}
1612
Vincent Cheng425d2b12020-05-01 23:35:38 -04001613static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1614{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001615 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Vincent Cheng425d2b12020-05-01 23:35:38 -04001616 struct idtcm *idtcm = channel->idtcm;
Vincent Cheng425d2b12020-05-01 23:35:38 -04001617 int err;
1618
1619 mutex_lock(&idtcm->reg_lock);
1620
1621 err = _idtcm_adjphase(channel, delta);
Min Li7ea5fda2020-07-28 16:00:30 -04001622 if (err)
1623 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001624 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001625
1626 mutex_unlock(&idtcm->reg_lock);
1627
1628 return err;
1629}
1630
1631static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
1632{
Vincent Chengfcfd3752021-02-17 00:42:16 -05001633 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Min Li7ea5fda2020-07-28 16:00:30 -04001634 struct idtcm *idtcm = channel->idtcm;
Min Li7ea5fda2020-07-28 16:00:30 -04001635 int err;
1636
1637 mutex_lock(&idtcm->reg_lock);
1638
1639 err = _idtcm_adjfine(channel, scaled_ppm);
Min Li7ea5fda2020-07-28 16:00:30 -04001640 if (err)
1641 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001642 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001643
Vincent Cheng425d2b12020-05-01 23:35:38 -04001644 mutex_unlock(&idtcm->reg_lock);
1645
1646 return err;
1647}
1648
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001649static int idtcm_enable(struct ptp_clock_info *ptp,
1650 struct ptp_clock_request *rq, int on)
1651{
Min Li7ea5fda2020-07-28 16:00:30 -04001652 int err;
Vincent Chengfcfd3752021-02-17 00:42:16 -05001653 struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001654
1655 switch (rq->type) {
1656 case PTP_CLK_REQ_PEROUT:
Min Li7ea5fda2020-07-28 16:00:30 -04001657 if (!on) {
1658 err = idtcm_perout_enable(channel, false, &rq->perout);
1659 if (err)
1660 dev_err(&channel->idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001661 "Failed at line %d in %s!",
1662 __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001663 return err;
1664 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001665
1666 /* Only accept a 1-PPS aligned to the second. */
1667 if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
1668 rq->perout.period.nsec)
1669 return -ERANGE;
1670
Min Li7ea5fda2020-07-28 16:00:30 -04001671 err = idtcm_perout_enable(channel, true, &rq->perout);
1672 if (err)
1673 dev_err(&channel->idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001674 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04001675 return err;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001676 default:
1677 break;
1678 }
1679
1680 return -EOPNOTSUPP;
1681}
1682
Min Li7ea5fda2020-07-28 16:00:30 -04001683static int _enable_pll_tod_sync(struct idtcm *idtcm,
1684 u8 pll,
1685 u8 sync_src,
1686 u8 qn,
1687 u8 qn_plus_1)
1688{
1689 int err;
1690 u8 val;
1691 u16 dpll;
1692 u16 out0 = 0, out1 = 0;
1693
1694 if ((qn == 0) && (qn_plus_1 == 0))
1695 return 0;
1696
1697 switch (pll) {
1698 case 0:
1699 dpll = DPLL_0;
1700 if (qn)
1701 out0 = OUTPUT_0;
1702 if (qn_plus_1)
1703 out1 = OUTPUT_1;
1704 break;
1705 case 1:
1706 dpll = DPLL_1;
1707 if (qn)
1708 out0 = OUTPUT_2;
1709 if (qn_plus_1)
1710 out1 = OUTPUT_3;
1711 break;
1712 case 2:
1713 dpll = DPLL_2;
1714 if (qn)
1715 out0 = OUTPUT_4;
1716 if (qn_plus_1)
1717 out1 = OUTPUT_5;
1718 break;
1719 case 3:
1720 dpll = DPLL_3;
1721 if (qn)
1722 out0 = OUTPUT_6;
1723 if (qn_plus_1)
1724 out1 = OUTPUT_7;
1725 break;
1726 case 4:
1727 dpll = DPLL_4;
1728 if (qn)
1729 out0 = OUTPUT_8;
1730 break;
1731 case 5:
1732 dpll = DPLL_5;
1733 if (qn)
1734 out0 = OUTPUT_9;
1735 if (qn_plus_1)
1736 out1 = OUTPUT_8;
1737 break;
1738 case 6:
1739 dpll = DPLL_6;
1740 if (qn)
1741 out0 = OUTPUT_10;
1742 if (qn_plus_1)
1743 out1 = OUTPUT_11;
1744 break;
1745 case 7:
1746 dpll = DPLL_7;
1747 if (qn)
1748 out0 = OUTPUT_11;
1749 break;
1750 default:
1751 return -EINVAL;
1752 }
1753
1754 /*
1755 * Enable OUTPUT OUT_SYNC.
1756 */
1757 if (out0) {
1758 err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
Min Li7ea5fda2020-07-28 16:00:30 -04001759 if (err)
1760 return err;
1761
1762 val &= ~OUT_SYNC_DISABLE;
1763
1764 err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
Min Li7ea5fda2020-07-28 16:00:30 -04001765 if (err)
1766 return err;
1767 }
1768
1769 if (out1) {
1770 err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
Min Li7ea5fda2020-07-28 16:00:30 -04001771 if (err)
1772 return err;
1773
1774 val &= ~OUT_SYNC_DISABLE;
1775
1776 err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
Min Li7ea5fda2020-07-28 16:00:30 -04001777 if (err)
1778 return err;
1779 }
1780
1781 /* enable dpll sync tod pps, must be set before dpll_mode */
1782 err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
1783 if (err)
1784 return err;
1785
1786 val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
1787 val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
1788 val |= TOD_SYNC_EN;
1789
1790 return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
1791}
1792
1793static int idtcm_enable_tod_sync(struct idtcm_channel *channel)
1794{
1795 struct idtcm *idtcm = channel->idtcm;
Min Li7ea5fda2020-07-28 16:00:30 -04001796 u8 pll;
1797 u8 sync_src;
1798 u8 qn;
1799 u8 qn_plus_1;
1800 u8 cfg;
1801 int err = 0;
1802 u16 output_mask = channel->output_mask;
1803 u8 out8_mux = 0;
1804 u8 out11_mux = 0;
1805 u8 temp;
1806
1807 /*
1808 * set tod_out_sync_enable to 0.
1809 */
1810 err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1811 if (err)
1812 return err;
1813
1814 cfg &= ~TOD_OUT_SYNC_ENABLE;
1815
1816 err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1817 if (err)
1818 return err;
1819
1820 switch (channel->tod_n) {
1821 case TOD_0:
1822 sync_src = 0;
1823 break;
1824 case TOD_1:
1825 sync_src = 1;
1826 break;
1827 case TOD_2:
1828 sync_src = 2;
1829 break;
1830 case TOD_3:
1831 sync_src = 3;
1832 break;
1833 default:
1834 return -EINVAL;
1835 }
1836
Vincent Chengfcfd3752021-02-17 00:42:16 -05001837 err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp));
Min Li7ea5fda2020-07-28 16:00:30 -04001838 if (err)
1839 return err;
1840
1841 if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
1842 Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
1843 out8_mux = 1;
1844
Vincent Chengfcfd3752021-02-17 00:42:16 -05001845 err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp));
Min Li7ea5fda2020-07-28 16:00:30 -04001846 if (err)
1847 return err;
1848
1849 if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
1850 Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
1851 out11_mux = 1;
1852
1853 for (pll = 0; pll < 8; pll++) {
1854 qn = 0;
1855 qn_plus_1 = 0;
1856
1857 if (pll < 4) {
1858 /* First 4 pll has 2 outputs */
1859 qn = output_mask & 0x1;
1860 output_mask = output_mask >> 1;
1861 qn_plus_1 = output_mask & 0x1;
1862 output_mask = output_mask >> 1;
1863 } else if (pll == 4) {
1864 if (out8_mux == 0) {
1865 qn = output_mask & 0x1;
1866 output_mask = output_mask >> 1;
1867 }
1868 } else if (pll == 5) {
1869 if (out8_mux) {
1870 qn_plus_1 = output_mask & 0x1;
1871 output_mask = output_mask >> 1;
1872 }
1873 qn = output_mask & 0x1;
1874 output_mask = output_mask >> 1;
1875 } else if (pll == 6) {
1876 qn = output_mask & 0x1;
1877 output_mask = output_mask >> 1;
1878 if (out11_mux) {
1879 qn_plus_1 = output_mask & 0x1;
1880 output_mask = output_mask >> 1;
1881 }
1882 } else if (pll == 7) {
1883 if (out11_mux == 0) {
1884 qn = output_mask & 0x1;
1885 output_mask = output_mask >> 1;
1886 }
1887 }
1888
1889 if ((qn != 0) || (qn_plus_1 != 0))
1890 err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
1891 qn_plus_1);
Min Li7ea5fda2020-07-28 16:00:30 -04001892 if (err)
1893 return err;
1894 }
1895
1896 return err;
1897}
1898
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001899static int idtcm_enable_tod(struct idtcm_channel *channel)
1900{
1901 struct idtcm *idtcm = channel->idtcm;
1902 struct timespec64 ts = {0, 0};
1903 u8 cfg;
1904 int err;
1905
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001906 /*
1907 * Start the TOD clock ticking.
1908 */
1909 err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1910 if (err)
1911 return err;
1912
1913 cfg |= TOD_ENABLE;
1914
1915 err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
1916 if (err)
1917 return err;
1918
Min Lida948232020-12-08 10:41:57 -05001919 if (idtcm->deprecated)
1920 return _idtcm_settime_deprecated(channel, &ts);
1921 else
1922 return _idtcm_settime(channel, &ts,
1923 SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001924}
1925
Min Lida948232020-12-08 10:41:57 -05001926static void idtcm_set_version_info(struct idtcm *idtcm)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001927{
1928 u8 major;
1929 u8 minor;
1930 u8 hotfix;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001931 u16 product_id;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001932 u8 hw_rev_id;
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001933 u8 config_select;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001934
1935 idtcm_read_major_release(idtcm, &major);
1936 idtcm_read_minor_release(idtcm, &minor);
1937 idtcm_read_hotfix_release(idtcm, &hotfix);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001938
1939 idtcm_read_product_id(idtcm, &product_id);
1940 idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001941
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001942 idtcm_read_otp_scsr_config_select(idtcm, &config_select);
1943
Min Li7ea5fda2020-07-28 16:00:30 -04001944 snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
1945 major, minor, hotfix);
1946
Min Lida948232020-12-08 10:41:57 -05001947 if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
1948 idtcm->deprecated = 0;
1949 else
1950 idtcm->deprecated = 1;
1951
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05001952 dev_info(&idtcm->client->dev,
1953 "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d",
1954 major, minor, hotfix,
Vincent Cheng1ece2fb2020-01-07 09:47:57 -05001955 product_id, hw_rev_id, config_select);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001956}
1957
Julia Lawall6485f9a2020-01-01 08:43:31 +01001958static const struct ptp_clock_info idtcm_caps = {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001959 .owner = THIS_MODULE,
1960 .max_adj = 244000,
Min Li7ea5fda2020-07-28 16:00:30 -04001961 .n_per_out = 12,
Vincent Cheng425d2b12020-05-01 23:35:38 -04001962 .adjphase = &idtcm_adjphase,
Min Li7ea5fda2020-07-28 16:00:30 -04001963 .adjfine = &idtcm_adjfine,
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001964 .adjtime = &idtcm_adjtime,
1965 .gettime64 = &idtcm_gettime,
1966 .settime64 = &idtcm_settime,
1967 .enable = &idtcm_enable,
1968};
1969
Min Lida948232020-12-08 10:41:57 -05001970static const struct ptp_clock_info idtcm_caps_deprecated = {
1971 .owner = THIS_MODULE,
1972 .max_adj = 244000,
1973 .n_per_out = 12,
1974 .adjphase = &idtcm_adjphase,
1975 .adjfine = &idtcm_adjfine,
1976 .adjtime = &idtcm_adjtime_deprecated,
1977 .gettime64 = &idtcm_gettime,
1978 .settime64 = &idtcm_settime_deprecated,
1979 .enable = &idtcm_enable,
1980};
1981
Min Li7ea5fda2020-07-28 16:00:30 -04001982static int configure_channel_pll(struct idtcm_channel *channel)
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001983{
Min Li7ea5fda2020-07-28 16:00:30 -04001984 int err = 0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001985
Min Li7ea5fda2020-07-28 16:00:30 -04001986 switch (channel->pll) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001987 case 0:
1988 channel->dpll_freq = DPLL_FREQ_0;
1989 channel->dpll_n = DPLL_0;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001990 channel->hw_dpll_n = HW_DPLL_0;
1991 channel->dpll_phase = DPLL_PHASE_0;
1992 channel->dpll_ctrl_n = DPLL_CTRL_0;
1993 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
1994 break;
1995 case 1:
1996 channel->dpll_freq = DPLL_FREQ_1;
1997 channel->dpll_n = DPLL_1;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04001998 channel->hw_dpll_n = HW_DPLL_1;
1999 channel->dpll_phase = DPLL_PHASE_1;
2000 channel->dpll_ctrl_n = DPLL_CTRL_1;
2001 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
2002 break;
2003 case 2:
2004 channel->dpll_freq = DPLL_FREQ_2;
2005 channel->dpll_n = DPLL_2;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002006 channel->hw_dpll_n = HW_DPLL_2;
2007 channel->dpll_phase = DPLL_PHASE_2;
2008 channel->dpll_ctrl_n = DPLL_CTRL_2;
2009 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
2010 break;
2011 case 3:
2012 channel->dpll_freq = DPLL_FREQ_3;
2013 channel->dpll_n = DPLL_3;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002014 channel->hw_dpll_n = HW_DPLL_3;
2015 channel->dpll_phase = DPLL_PHASE_3;
2016 channel->dpll_ctrl_n = DPLL_CTRL_3;
2017 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
2018 break;
Min Li7ea5fda2020-07-28 16:00:30 -04002019 case 4:
2020 channel->dpll_freq = DPLL_FREQ_4;
2021 channel->dpll_n = DPLL_4;
2022 channel->hw_dpll_n = HW_DPLL_4;
2023 channel->dpll_phase = DPLL_PHASE_4;
2024 channel->dpll_ctrl_n = DPLL_CTRL_4;
2025 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
2026 break;
2027 case 5:
2028 channel->dpll_freq = DPLL_FREQ_5;
2029 channel->dpll_n = DPLL_5;
2030 channel->hw_dpll_n = HW_DPLL_5;
2031 channel->dpll_phase = DPLL_PHASE_5;
2032 channel->dpll_ctrl_n = DPLL_CTRL_5;
2033 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
2034 break;
2035 case 6:
2036 channel->dpll_freq = DPLL_FREQ_6;
2037 channel->dpll_n = DPLL_6;
2038 channel->hw_dpll_n = HW_DPLL_6;
2039 channel->dpll_phase = DPLL_PHASE_6;
2040 channel->dpll_ctrl_n = DPLL_CTRL_6;
2041 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
2042 break;
2043 case 7:
2044 channel->dpll_freq = DPLL_FREQ_7;
2045 channel->dpll_n = DPLL_7;
2046 channel->hw_dpll_n = HW_DPLL_7;
2047 channel->dpll_phase = DPLL_PHASE_7;
2048 channel->dpll_ctrl_n = DPLL_CTRL_7;
2049 channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
2050 break;
2051 default:
2052 err = -EINVAL;
2053 }
2054
2055 return err;
2056}
2057
2058static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
2059{
2060 struct idtcm_channel *channel;
2061 int err;
2062
2063 if (!(index < MAX_TOD))
2064 return -EINVAL;
2065
2066 channel = &idtcm->channel[index];
2067
2068 /* Set pll addresses */
2069 err = configure_channel_pll(channel);
2070 if (err)
2071 return err;
2072
2073 /* Set tod addresses */
2074 switch (index) {
2075 case 0:
2076 channel->tod_read_primary = TOD_READ_PRIMARY_0;
2077 channel->tod_write = TOD_WRITE_0;
2078 channel->tod_n = TOD_0;
2079 break;
2080 case 1:
2081 channel->tod_read_primary = TOD_READ_PRIMARY_1;
2082 channel->tod_write = TOD_WRITE_1;
2083 channel->tod_n = TOD_1;
2084 break;
2085 case 2:
2086 channel->tod_read_primary = TOD_READ_PRIMARY_2;
2087 channel->tod_write = TOD_WRITE_2;
2088 channel->tod_n = TOD_2;
2089 break;
2090 case 3:
2091 channel->tod_read_primary = TOD_READ_PRIMARY_3;
2092 channel->tod_write = TOD_WRITE_3;
2093 channel->tod_n = TOD_3;
2094 break;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002095 default:
2096 return -EINVAL;
2097 }
2098
2099 channel->idtcm = idtcm;
2100
Min Lida948232020-12-08 10:41:57 -05002101 if (idtcm->deprecated)
2102 channel->caps = idtcm_caps_deprecated;
Min Li7ea5fda2020-07-28 16:00:30 -04002103 else
2104 channel->caps = idtcm_caps;
2105
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002106 snprintf(channel->caps.name, sizeof(channel->caps.name),
Min Li7ea5fda2020-07-28 16:00:30 -04002107 "IDT CM TOD%u", index);
2108
Min Lida948232020-12-08 10:41:57 -05002109 if (!idtcm->deprecated) {
Min Li7ea5fda2020-07-28 16:00:30 -04002110 err = idtcm_enable_tod_sync(channel);
2111 if (err) {
2112 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002113 "Failed at line %d in %s!", __LINE__, __func__);
Min Li7ea5fda2020-07-28 16:00:30 -04002114 return err;
2115 }
2116 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002117
Min Li7260d1c2020-12-08 10:41:56 -05002118 /* Sync pll mode with hardware */
2119 err = idtcm_get_pll_mode(channel, &channel->pll_mode);
Min Li7ea5fda2020-07-28 16:00:30 -04002120 if (err) {
2121 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002122 "Error: %s - Unable to read pll mode", __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002123 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04002124 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002125
2126 err = idtcm_enable_tod(channel);
Min Li7ea5fda2020-07-28 16:00:30 -04002127 if (err) {
2128 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002129 "Failed at line %d in %s!", __LINE__, __func__);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002130 return err;
Min Li7ea5fda2020-07-28 16:00:30 -04002131 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002132
2133 channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
2134
2135 if (IS_ERR(channel->ptp_clock)) {
2136 err = PTR_ERR(channel->ptp_clock);
2137 channel->ptp_clock = NULL;
2138 return err;
2139 }
2140
2141 if (!channel->ptp_clock)
2142 return -ENOTSUPP;
2143
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002144 dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d",
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002145 index, channel->ptp_clock->index);
2146
2147 return 0;
2148}
2149
2150static void ptp_clock_unregister_all(struct idtcm *idtcm)
2151{
2152 u8 i;
2153 struct idtcm_channel *channel;
2154
Min Li7ea5fda2020-07-28 16:00:30 -04002155 for (i = 0; i < MAX_TOD; i++) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002156 channel = &idtcm->channel[i];
2157
2158 if (channel->ptp_clock)
2159 ptp_clock_unregister(channel->ptp_clock);
2160 }
2161}
2162
2163static void set_default_masks(struct idtcm *idtcm)
2164{
Min Li7ea5fda2020-07-28 16:00:30 -04002165 idtcm->tod_mask = DEFAULT_TOD_MASK;
2166
2167 idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
2168 idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
2169 idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
2170 idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002171
2172 idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
2173 idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
2174 idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
2175 idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
2176}
2177
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002178static int idtcm_probe(struct i2c_client *client,
2179 const struct i2c_device_id *id)
2180{
2181 struct idtcm *idtcm;
2182 int err;
2183 u8 i;
2184
2185 /* Unused for now */
2186 (void)id;
2187
2188 idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
2189
2190 if (!idtcm)
2191 return -ENOMEM;
2192
2193 idtcm->client = client;
2194 idtcm->page_offset = 0xff;
2195 idtcm->calculate_overhead_flag = 0;
2196
2197 set_default_masks(idtcm);
2198
2199 mutex_init(&idtcm->reg_lock);
2200 mutex_lock(&idtcm->reg_lock);
2201
Min Lida948232020-12-08 10:41:57 -05002202 idtcm_set_version_info(idtcm);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002203
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002204 err = idtcm_load_firmware(idtcm, &client->dev);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002205 if (err)
Vincent Chengfcfd3752021-02-17 00:42:16 -05002206 dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002207
Vincent Cheng797d3182021-02-17 00:42:12 -05002208 wait_for_chip_ready(idtcm);
Min Li251f4fe2020-12-08 10:41:54 -05002209
Min Li7ea5fda2020-07-28 16:00:30 -04002210 if (idtcm->tod_mask) {
2211 for (i = 0; i < MAX_TOD; i++) {
2212 if (idtcm->tod_mask & (1 << i)) {
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002213 err = idtcm_enable_channel(idtcm, i);
Min Li7ea5fda2020-07-28 16:00:30 -04002214 if (err) {
2215 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002216 "idtcm_enable_channel %d failed!", i);
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002217 break;
Min Li7ea5fda2020-07-28 16:00:30 -04002218 }
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002219 }
2220 }
2221 } else {
2222 dev_err(&idtcm->client->dev,
Vincent Cheng1c49d3e2021-02-17 00:42:15 -05002223 "no PLLs flagged as PHCs, nothing to do");
Vincent Cheng3a6ba7d2019-10-31 23:20:07 -04002224 err = -ENODEV;
2225 }
2226
2227 mutex_unlock(&idtcm->reg_lock);
2228
2229 if (err) {
2230 ptp_clock_unregister_all(idtcm);
2231 return err;
2232 }
2233
2234 i2c_set_clientdata(client, idtcm);
2235
2236 return 0;
2237}
2238
2239static int idtcm_remove(struct i2c_client *client)
2240{
2241 struct idtcm *idtcm = i2c_get_clientdata(client);
2242
2243 ptp_clock_unregister_all(idtcm);
2244
2245 mutex_destroy(&idtcm->reg_lock);
2246
2247 return 0;
2248}
2249
2250#ifdef CONFIG_OF
2251static const struct of_device_id idtcm_dt_id[] = {
2252 { .compatible = "idt,8a34000" },
2253 { .compatible = "idt,8a34001" },
2254 { .compatible = "idt,8a34002" },
2255 { .compatible = "idt,8a34003" },
2256 { .compatible = "idt,8a34004" },
2257 { .compatible = "idt,8a34005" },
2258 { .compatible = "idt,8a34006" },
2259 { .compatible = "idt,8a34007" },
2260 { .compatible = "idt,8a34008" },
2261 { .compatible = "idt,8a34009" },
2262 { .compatible = "idt,8a34010" },
2263 { .compatible = "idt,8a34011" },
2264 { .compatible = "idt,8a34012" },
2265 { .compatible = "idt,8a34013" },
2266 { .compatible = "idt,8a34014" },
2267 { .compatible = "idt,8a34015" },
2268 { .compatible = "idt,8a34016" },
2269 { .compatible = "idt,8a34017" },
2270 { .compatible = "idt,8a34018" },
2271 { .compatible = "idt,8a34019" },
2272 { .compatible = "idt,8a34040" },
2273 { .compatible = "idt,8a34041" },
2274 { .compatible = "idt,8a34042" },
2275 { .compatible = "idt,8a34043" },
2276 { .compatible = "idt,8a34044" },
2277 { .compatible = "idt,8a34045" },
2278 { .compatible = "idt,8a34046" },
2279 { .compatible = "idt,8a34047" },
2280 { .compatible = "idt,8a34048" },
2281 { .compatible = "idt,8a34049" },
2282 {},
2283};
2284MODULE_DEVICE_TABLE(of, idtcm_dt_id);
2285#endif
2286
2287static const struct i2c_device_id idtcm_i2c_id[] = {
2288 { "8a34000" },
2289 { "8a34001" },
2290 { "8a34002" },
2291 { "8a34003" },
2292 { "8a34004" },
2293 { "8a34005" },
2294 { "8a34006" },
2295 { "8a34007" },
2296 { "8a34008" },
2297 { "8a34009" },
2298 { "8a34010" },
2299 { "8a34011" },
2300 { "8a34012" },
2301 { "8a34013" },
2302 { "8a34014" },
2303 { "8a34015" },
2304 { "8a34016" },
2305 { "8a34017" },
2306 { "8a34018" },
2307 { "8a34019" },
2308 { "8a34040" },
2309 { "8a34041" },
2310 { "8a34042" },
2311 { "8a34043" },
2312 { "8a34044" },
2313 { "8a34045" },
2314 { "8a34046" },
2315 { "8a34047" },
2316 { "8a34048" },
2317 { "8a34049" },
2318 {},
2319};
2320MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
2321
2322static struct i2c_driver idtcm_driver = {
2323 .driver = {
2324 .of_match_table = of_match_ptr(idtcm_dt_id),
2325 .name = "idtcm",
2326 },
2327 .probe = idtcm_probe,
2328 .remove = idtcm_remove,
2329 .id_table = idtcm_i2c_id,
2330};
2331
2332module_i2c_driver(idtcm_driver);