xref: /openbmc/linux/drivers/ptp/ptp_clockmatrix.c (revision da948233)
13a6ba7dcSVincent Cheng // SPDX-License-Identifier: GPL-2.0+
23a6ba7dcSVincent Cheng /*
33a6ba7dcSVincent Cheng  * PTP hardware clock driver for the IDT ClockMatrix(TM) family of timing and
43a6ba7dcSVincent Cheng  * synchronization devices.
53a6ba7dcSVincent Cheng  *
63a6ba7dcSVincent Cheng  * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
73a6ba7dcSVincent Cheng  */
83a6ba7dcSVincent Cheng #include <linux/firmware.h>
93a6ba7dcSVincent Cheng #include <linux/i2c.h>
103a6ba7dcSVincent Cheng #include <linux/module.h>
113a6ba7dcSVincent Cheng #include <linux/ptp_clock_kernel.h>
123a6ba7dcSVincent Cheng #include <linux/delay.h>
13425d2b1cSVincent Cheng #include <linux/jiffies.h>
143a6ba7dcSVincent Cheng #include <linux/kernel.h>
153a6ba7dcSVincent Cheng #include <linux/timekeeping.h>
167ea5fda2SMin Li #include <linux/string.h>
173a6ba7dcSVincent Cheng 
183a6ba7dcSVincent Cheng #include "ptp_private.h"
193a6ba7dcSVincent Cheng #include "ptp_clockmatrix.h"
203a6ba7dcSVincent Cheng 
213a6ba7dcSVincent Cheng MODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family");
223a6ba7dcSVincent Cheng MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
233a6ba7dcSVincent Cheng MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
243a6ba7dcSVincent Cheng MODULE_VERSION("1.0");
253a6ba7dcSVincent Cheng MODULE_LICENSE("GPL");
263a6ba7dcSVincent Cheng 
277ea5fda2SMin Li /*
287ea5fda2SMin Li  * The name of the firmware file to be loaded
297ea5fda2SMin Li  * over-rides any automatic selection
307ea5fda2SMin Li  */
317ea5fda2SMin Li static char *firmware;
327ea5fda2SMin Li module_param(firmware, charp, 0);
337ea5fda2SMin Li 
343a6ba7dcSVincent Cheng #define SETTIME_CORRECTION (0)
353a6ba7dcSVincent Cheng 
36251f4fe2SMin Li static int contains_full_configuration(const struct firmware *fw)
37251f4fe2SMin Li {
38251f4fe2SMin Li 	s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES;
39251f4fe2SMin Li 	struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data;
40251f4fe2SMin Li 	s32 count = 0;
41251f4fe2SMin Li 	u16 regaddr;
42251f4fe2SMin Li 	u8 loaddr;
43251f4fe2SMin Li 	s32 len;
44251f4fe2SMin Li 
45251f4fe2SMin Li 	/* If the firmware contains 'full configuration' SM_RESET can be used
46251f4fe2SMin Li 	 * to ensure proper configuration.
47251f4fe2SMin Li 	 *
48251f4fe2SMin Li 	 * Full configuration is defined as the number of programmable
49251f4fe2SMin Li 	 * bytes within the configuration range minus page offset addr range.
50251f4fe2SMin Li 	 */
51251f4fe2SMin Li 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
52251f4fe2SMin Li 		regaddr = rec->hiaddr << 8;
53251f4fe2SMin Li 		regaddr |= rec->loaddr;
54251f4fe2SMin Li 
55251f4fe2SMin Li 		loaddr = rec->loaddr;
56251f4fe2SMin Li 
57251f4fe2SMin Li 		rec++;
58251f4fe2SMin Li 
59251f4fe2SMin Li 		/* Top (status registers) and bottom are read-only */
60251f4fe2SMin Li 		if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH)
61251f4fe2SMin Li 			continue;
62251f4fe2SMin Li 
63251f4fe2SMin Li 		/* Page size 128, last 4 bytes of page skipped */
64251f4fe2SMin Li 		if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
65251f4fe2SMin Li 			continue;
66251f4fe2SMin Li 
67251f4fe2SMin Li 		count++;
68251f4fe2SMin Li 	}
69251f4fe2SMin Li 
70251f4fe2SMin Li 	return (count >= full_count);
71251f4fe2SMin Li }
72251f4fe2SMin Li 
733a6ba7dcSVincent Cheng static int char_array_to_timespec(u8 *buf,
743a6ba7dcSVincent Cheng 				  u8 count,
753a6ba7dcSVincent Cheng 				  struct timespec64 *ts)
763a6ba7dcSVincent Cheng {
773a6ba7dcSVincent Cheng 	u8 i;
783a6ba7dcSVincent Cheng 	u64 nsec;
793a6ba7dcSVincent Cheng 	time64_t sec;
803a6ba7dcSVincent Cheng 
813a6ba7dcSVincent Cheng 	if (count < TOD_BYTE_COUNT)
823a6ba7dcSVincent Cheng 		return 1;
833a6ba7dcSVincent Cheng 
843a6ba7dcSVincent Cheng 	/* Sub-nanoseconds are in buf[0]. */
853a6ba7dcSVincent Cheng 	nsec = buf[4];
863a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
873a6ba7dcSVincent Cheng 		nsec <<= 8;
883a6ba7dcSVincent Cheng 		nsec |= buf[3 - i];
893a6ba7dcSVincent Cheng 	}
903a6ba7dcSVincent Cheng 
913a6ba7dcSVincent Cheng 	sec = buf[10];
923a6ba7dcSVincent Cheng 	for (i = 0; i < 5; i++) {
933a6ba7dcSVincent Cheng 		sec <<= 8;
943a6ba7dcSVincent Cheng 		sec |= buf[9 - i];
953a6ba7dcSVincent Cheng 	}
963a6ba7dcSVincent Cheng 
973a6ba7dcSVincent Cheng 	ts->tv_sec = sec;
983a6ba7dcSVincent Cheng 	ts->tv_nsec = nsec;
993a6ba7dcSVincent Cheng 
1003a6ba7dcSVincent Cheng 	return 0;
1013a6ba7dcSVincent Cheng }
1023a6ba7dcSVincent Cheng 
1033a6ba7dcSVincent Cheng static int timespec_to_char_array(struct timespec64 const *ts,
1043a6ba7dcSVincent Cheng 				  u8 *buf,
1053a6ba7dcSVincent Cheng 				  u8 count)
1063a6ba7dcSVincent Cheng {
1073a6ba7dcSVincent Cheng 	u8 i;
1083a6ba7dcSVincent Cheng 	s32 nsec;
1093a6ba7dcSVincent Cheng 	time64_t sec;
1103a6ba7dcSVincent Cheng 
1113a6ba7dcSVincent Cheng 	if (count < TOD_BYTE_COUNT)
1123a6ba7dcSVincent Cheng 		return 1;
1133a6ba7dcSVincent Cheng 
1143a6ba7dcSVincent Cheng 	nsec = ts->tv_nsec;
1153a6ba7dcSVincent Cheng 	sec = ts->tv_sec;
1163a6ba7dcSVincent Cheng 
1173a6ba7dcSVincent Cheng 	/* Sub-nanoseconds are in buf[0]. */
1183a6ba7dcSVincent Cheng 	buf[0] = 0;
1193a6ba7dcSVincent Cheng 	for (i = 1; i < 5; i++) {
1203a6ba7dcSVincent Cheng 		buf[i] = nsec & 0xff;
1213a6ba7dcSVincent Cheng 		nsec >>= 8;
1223a6ba7dcSVincent Cheng 	}
1233a6ba7dcSVincent Cheng 
1243a6ba7dcSVincent Cheng 	for (i = 5; i < TOD_BYTE_COUNT; i++) {
1253a6ba7dcSVincent Cheng 
1263a6ba7dcSVincent Cheng 		buf[i] = sec & 0xff;
1273a6ba7dcSVincent Cheng 		sec >>= 8;
1283a6ba7dcSVincent Cheng 	}
1293a6ba7dcSVincent Cheng 
1303a6ba7dcSVincent Cheng 	return 0;
1313a6ba7dcSVincent Cheng }
1323a6ba7dcSVincent Cheng 
1333cb2e6d9SMin Li static int idtcm_strverscmp(const char *version1, const char *version2)
1347ea5fda2SMin Li {
1353cb2e6d9SMin Li 	u8 ver1[3], ver2[3];
1363cb2e6d9SMin Li 	int i;
1377ea5fda2SMin Li 
1383cb2e6d9SMin Li 	if (sscanf(version1, "%hhu.%hhu.%hhu",
1393cb2e6d9SMin Li 		   &ver1[0], &ver1[1], &ver1[2]) != 3)
1403cb2e6d9SMin Li 		return -1;
1413cb2e6d9SMin Li 	if (sscanf(version2, "%hhu.%hhu.%hhu",
1423cb2e6d9SMin Li 		   &ver2[0], &ver2[1], &ver2[2]) != 3)
1437ea5fda2SMin Li 		return -1;
1447ea5fda2SMin Li 
1453cb2e6d9SMin Li 	for (i = 0; i < 3; i++) {
1463cb2e6d9SMin Li 		if (ver1[i] > ver2[i])
1473cb2e6d9SMin Li 			return 1;
1483cb2e6d9SMin Li 		if (ver1[i] < ver2[i])
1497ea5fda2SMin Li 			return -1;
1503cb2e6d9SMin Li 	}
1517ea5fda2SMin Li 
1523cb2e6d9SMin Li 	return 0;
1537ea5fda2SMin Li }
1547ea5fda2SMin Li 
155957ff427SMin Li static int idtcm_xfer_read(struct idtcm *idtcm,
1563a6ba7dcSVincent Cheng 			   u8 regaddr,
1573a6ba7dcSVincent Cheng 			   u8 *buf,
158957ff427SMin Li 			   u16 count)
1593a6ba7dcSVincent Cheng {
1603a6ba7dcSVincent Cheng 	struct i2c_client *client = idtcm->client;
1613a6ba7dcSVincent Cheng 	struct i2c_msg msg[2];
1623a6ba7dcSVincent Cheng 	int cnt;
163957ff427SMin Li 	char *fmt = "i2c_transfer failed at %d in %s, at addr: %04X!\n";
1643a6ba7dcSVincent Cheng 
1653a6ba7dcSVincent Cheng 	msg[0].addr = client->addr;
1663a6ba7dcSVincent Cheng 	msg[0].flags = 0;
1673a6ba7dcSVincent Cheng 	msg[0].len = 1;
1683a6ba7dcSVincent Cheng 	msg[0].buf = &regaddr;
1693a6ba7dcSVincent Cheng 
1703a6ba7dcSVincent Cheng 	msg[1].addr = client->addr;
171957ff427SMin Li 	msg[1].flags = I2C_M_RD;
1723a6ba7dcSVincent Cheng 	msg[1].len = count;
1733a6ba7dcSVincent Cheng 	msg[1].buf = buf;
1743a6ba7dcSVincent Cheng 
1753a6ba7dcSVincent Cheng 	cnt = i2c_transfer(client->adapter, msg, 2);
1763a6ba7dcSVincent Cheng 
1773a6ba7dcSVincent Cheng 	if (cnt < 0) {
1787ea5fda2SMin Li 		dev_err(&client->dev,
1797ea5fda2SMin Li 			fmt,
1807ea5fda2SMin Li 			__LINE__,
1817ea5fda2SMin Li 			__func__,
1827ea5fda2SMin Li 			regaddr);
1833a6ba7dcSVincent Cheng 		return cnt;
1843a6ba7dcSVincent Cheng 	} else if (cnt != 2) {
1853a6ba7dcSVincent Cheng 		dev_err(&client->dev,
1863a6ba7dcSVincent Cheng 			"i2c_transfer sent only %d of %d messages\n", cnt, 2);
1873a6ba7dcSVincent Cheng 		return -EIO;
1883a6ba7dcSVincent Cheng 	}
1893a6ba7dcSVincent Cheng 
1903a6ba7dcSVincent Cheng 	return 0;
1913a6ba7dcSVincent Cheng }
1923a6ba7dcSVincent Cheng 
193957ff427SMin Li static int idtcm_xfer_write(struct idtcm *idtcm,
194957ff427SMin Li 			    u8 regaddr,
195957ff427SMin Li 			    u8 *buf,
196957ff427SMin Li 			    u16 count)
197957ff427SMin Li {
198957ff427SMin Li 	struct i2c_client *client = idtcm->client;
199957ff427SMin Li 	/* we add 1 byte for device register */
200957ff427SMin Li 	u8 msg[IDTCM_MAX_WRITE_COUNT + 1];
201957ff427SMin Li 	int cnt;
202957ff427SMin Li 	char *fmt = "i2c_master_send failed at %d in %s, at addr: %04X!\n";
203957ff427SMin Li 
204957ff427SMin Li 	if (count > IDTCM_MAX_WRITE_COUNT)
205957ff427SMin Li 		return -EINVAL;
206957ff427SMin Li 
207957ff427SMin Li 	msg[0] = regaddr;
208957ff427SMin Li 	memcpy(&msg[1], buf, count);
209957ff427SMin Li 
210957ff427SMin Li 	cnt = i2c_master_send(client, msg, count + 1);
211957ff427SMin Li 
212957ff427SMin Li 	if (cnt < 0) {
213957ff427SMin Li 		dev_err(&client->dev,
214957ff427SMin Li 			fmt,
215957ff427SMin Li 			__LINE__,
216957ff427SMin Li 			__func__,
217957ff427SMin Li 			regaddr);
218957ff427SMin Li 		return cnt;
219957ff427SMin Li 	}
220957ff427SMin Li 
221957ff427SMin Li 	return 0;
222957ff427SMin Li }
223957ff427SMin Li 
2243a6ba7dcSVincent Cheng static int idtcm_page_offset(struct idtcm *idtcm, u8 val)
2253a6ba7dcSVincent Cheng {
2263a6ba7dcSVincent Cheng 	u8 buf[4];
2273a6ba7dcSVincent Cheng 	int err;
2283a6ba7dcSVincent Cheng 
2293a6ba7dcSVincent Cheng 	if (idtcm->page_offset == val)
2303a6ba7dcSVincent Cheng 		return 0;
2313a6ba7dcSVincent Cheng 
2323a6ba7dcSVincent Cheng 	buf[0] = 0x0;
2333a6ba7dcSVincent Cheng 	buf[1] = val;
2343a6ba7dcSVincent Cheng 	buf[2] = 0x10;
2353a6ba7dcSVincent Cheng 	buf[3] = 0x20;
2363a6ba7dcSVincent Cheng 
237957ff427SMin Li 	err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf));
2383a6ba7dcSVincent Cheng 
2397ea5fda2SMin Li 	if (err) {
2407ea5fda2SMin Li 		idtcm->page_offset = 0xff;
2413a6ba7dcSVincent Cheng 		dev_err(&idtcm->client->dev, "failed to set page offset\n");
2427ea5fda2SMin Li 	} else {
2433a6ba7dcSVincent Cheng 		idtcm->page_offset = val;
2447ea5fda2SMin Li 	}
2453a6ba7dcSVincent Cheng 
2463a6ba7dcSVincent Cheng 	return err;
2473a6ba7dcSVincent Cheng }
2483a6ba7dcSVincent Cheng 
2493a6ba7dcSVincent Cheng static int _idtcm_rdwr(struct idtcm *idtcm,
2503a6ba7dcSVincent Cheng 		       u16 regaddr,
2513a6ba7dcSVincent Cheng 		       u8 *buf,
2523a6ba7dcSVincent Cheng 		       u16 count,
2533a6ba7dcSVincent Cheng 		       bool write)
2543a6ba7dcSVincent Cheng {
2553a6ba7dcSVincent Cheng 	u8 hi;
2563a6ba7dcSVincent Cheng 	u8 lo;
2573a6ba7dcSVincent Cheng 	int err;
2583a6ba7dcSVincent Cheng 
2593a6ba7dcSVincent Cheng 	hi = (regaddr >> 8) & 0xff;
2603a6ba7dcSVincent Cheng 	lo = regaddr & 0xff;
2613a6ba7dcSVincent Cheng 
2623a6ba7dcSVincent Cheng 	err = idtcm_page_offset(idtcm, hi);
2633a6ba7dcSVincent Cheng 
2643a6ba7dcSVincent Cheng 	if (err)
2653a6ba7dcSVincent Cheng 		return err;
266957ff427SMin Li 
267957ff427SMin Li 	if (write)
268957ff427SMin Li 		return idtcm_xfer_write(idtcm, lo, buf, count);
269957ff427SMin Li 
270957ff427SMin Li 	return idtcm_xfer_read(idtcm, lo, buf, count);
2713a6ba7dcSVincent Cheng }
2723a6ba7dcSVincent Cheng 
2733a6ba7dcSVincent Cheng static int idtcm_read(struct idtcm *idtcm,
2743a6ba7dcSVincent Cheng 		      u16 module,
2753a6ba7dcSVincent Cheng 		      u16 regaddr,
2763a6ba7dcSVincent Cheng 		      u8 *buf,
2773a6ba7dcSVincent Cheng 		      u16 count)
2783a6ba7dcSVincent Cheng {
2793a6ba7dcSVincent Cheng 	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false);
2803a6ba7dcSVincent Cheng }
2813a6ba7dcSVincent Cheng 
2823a6ba7dcSVincent Cheng static int idtcm_write(struct idtcm *idtcm,
2833a6ba7dcSVincent Cheng 		       u16 module,
2843a6ba7dcSVincent Cheng 		       u16 regaddr,
2853a6ba7dcSVincent Cheng 		       u8 *buf,
2863a6ba7dcSVincent Cheng 		       u16 count)
2873a6ba7dcSVincent Cheng {
2883a6ba7dcSVincent Cheng 	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
2893a6ba7dcSVincent Cheng }
2903a6ba7dcSVincent Cheng 
291251f4fe2SMin Li static int clear_boot_status(struct idtcm *idtcm)
292251f4fe2SMin Li {
293251f4fe2SMin Li 	int err;
294251f4fe2SMin Li 	u8 buf[4] = {0};
295251f4fe2SMin Li 
296251f4fe2SMin Li 	err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
297251f4fe2SMin Li 
298251f4fe2SMin Li 	return err;
299251f4fe2SMin Li }
300251f4fe2SMin Li 
301251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status)
302251f4fe2SMin Li {
303251f4fe2SMin Li 	int err;
304251f4fe2SMin Li 	u8 buf[4] = {0};
305251f4fe2SMin Li 
306251f4fe2SMin Li 	err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
307251f4fe2SMin Li 
308251f4fe2SMin Li 	*status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
309251f4fe2SMin Li 
310251f4fe2SMin Li 	return err;
311251f4fe2SMin Li }
312251f4fe2SMin Li 
313251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm)
314251f4fe2SMin Li {
315251f4fe2SMin Li 	u32 status = 0;
316251f4fe2SMin Li 	u8 i = 30;	/* 30 * 100ms = 3s */
317251f4fe2SMin Li 	int err;
318251f4fe2SMin Li 
319251f4fe2SMin Li 	do {
320251f4fe2SMin Li 		err = read_boot_status(idtcm, &status);
321251f4fe2SMin Li 
322251f4fe2SMin Li 		if (err)
323251f4fe2SMin Li 			return err;
324251f4fe2SMin Li 
325251f4fe2SMin Li 		if (status == 0xA0)
326251f4fe2SMin Li 			return 0;
327251f4fe2SMin Li 
328251f4fe2SMin Li 		msleep(100);
329251f4fe2SMin Li 		i--;
330251f4fe2SMin Li 
331251f4fe2SMin Li 	} while (i);
332251f4fe2SMin Li 
333251f4fe2SMin Li 	dev_warn(&idtcm->client->dev, "%s timed out\n", __func__);
334251f4fe2SMin Li 
335251f4fe2SMin Li 	return -EBUSY;
336251f4fe2SMin Li }
337251f4fe2SMin Li 
3383a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel,
3393a6ba7dcSVincent Cheng 			  struct timespec64 *ts)
3403a6ba7dcSVincent Cheng {
3413a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
3423a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
3437ea5fda2SMin Li 	u8 timeout = 10;
3443a6ba7dcSVincent Cheng 	u8 trigger;
3453a6ba7dcSVincent Cheng 	int err;
3463a6ba7dcSVincent Cheng 
3473a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
3483a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
3493a6ba7dcSVincent Cheng 	if (err)
3503a6ba7dcSVincent Cheng 		return err;
3513a6ba7dcSVincent Cheng 
3523a6ba7dcSVincent Cheng 	trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
3533a6ba7dcSVincent Cheng 	trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
3547ea5fda2SMin Li 	trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
3553a6ba7dcSVincent Cheng 
3563a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_read_primary,
3573a6ba7dcSVincent Cheng 			  TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
3587ea5fda2SMin Li 	if (err)
3597ea5fda2SMin Li 		return err;
3607ea5fda2SMin Li 
3617ea5fda2SMin Li 	/* wait trigger to be 0 */
3627ea5fda2SMin Li 	while (trigger & TOD_READ_TRIGGER_MASK) {
3637ea5fda2SMin Li 
3647ea5fda2SMin Li 		if (idtcm->calculate_overhead_flag)
3657ea5fda2SMin Li 			idtcm->start_time = ktime_get_raw();
3667ea5fda2SMin Li 
3677ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_read_primary,
3687ea5fda2SMin Li 				 TOD_READ_PRIMARY_CMD, &trigger,
3697ea5fda2SMin Li 				 sizeof(trigger));
3703a6ba7dcSVincent Cheng 
3713a6ba7dcSVincent Cheng 		if (err)
3723a6ba7dcSVincent Cheng 			return err;
3733a6ba7dcSVincent Cheng 
3747ea5fda2SMin Li 		if (--timeout == 0)
3757ea5fda2SMin Li 			return -EIO;
3767ea5fda2SMin Li 	}
3773a6ba7dcSVincent Cheng 
3783a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
3793a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY, buf, sizeof(buf));
3803a6ba7dcSVincent Cheng 
3813a6ba7dcSVincent Cheng 	if (err)
3823a6ba7dcSVincent Cheng 		return err;
3833a6ba7dcSVincent Cheng 
3843a6ba7dcSVincent Cheng 	err = char_array_to_timespec(buf, sizeof(buf), ts);
3853a6ba7dcSVincent Cheng 
3863a6ba7dcSVincent Cheng 	return err;
3873a6ba7dcSVincent Cheng }
3883a6ba7dcSVincent Cheng 
3893a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm,
3903a6ba7dcSVincent Cheng 			    u8 pll,
3913a6ba7dcSVincent Cheng 			    u8 sync_src,
3923a6ba7dcSVincent Cheng 			    u8 qn,
3933a6ba7dcSVincent Cheng 			    u8 qn_plus_1)
3943a6ba7dcSVincent Cheng {
3953a6ba7dcSVincent Cheng 	int err;
3963a6ba7dcSVincent Cheng 	u8 val;
3973a6ba7dcSVincent Cheng 	u16 sync_ctrl0;
3983a6ba7dcSVincent Cheng 	u16 sync_ctrl1;
3997ea5fda2SMin Li 	u8 temp;
4003a6ba7dcSVincent Cheng 
4013a6ba7dcSVincent Cheng 	if ((qn == 0) && (qn_plus_1 == 0))
4023a6ba7dcSVincent Cheng 		return 0;
4033a6ba7dcSVincent Cheng 
4043a6ba7dcSVincent Cheng 	switch (pll) {
4053a6ba7dcSVincent Cheng 	case 0:
4063a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
4073a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
4083a6ba7dcSVincent Cheng 		break;
4093a6ba7dcSVincent Cheng 	case 1:
4103a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
4113a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
4123a6ba7dcSVincent Cheng 		break;
4133a6ba7dcSVincent Cheng 	case 2:
4143a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
4153a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
4163a6ba7dcSVincent Cheng 		break;
4173a6ba7dcSVincent Cheng 	case 3:
4183a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
4193a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
4203a6ba7dcSVincent Cheng 		break;
4213a6ba7dcSVincent Cheng 	case 4:
4223a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
4233a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
4243a6ba7dcSVincent Cheng 		break;
4253a6ba7dcSVincent Cheng 	case 5:
4263a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
4273a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
4283a6ba7dcSVincent Cheng 		break;
4293a6ba7dcSVincent Cheng 	case 6:
4303a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
4313a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
4323a6ba7dcSVincent Cheng 		break;
4333a6ba7dcSVincent Cheng 	case 7:
4343a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
4353a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
4363a6ba7dcSVincent Cheng 		break;
4373a6ba7dcSVincent Cheng 	default:
4383a6ba7dcSVincent Cheng 		return -EINVAL;
4393a6ba7dcSVincent Cheng 	}
4403a6ba7dcSVincent Cheng 
4413a6ba7dcSVincent Cheng 	val = SYNCTRL1_MASTER_SYNC_RST;
4423a6ba7dcSVincent Cheng 
4433a6ba7dcSVincent Cheng 	/* Place master sync in reset */
4443a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
4453a6ba7dcSVincent Cheng 	if (err)
4463a6ba7dcSVincent Cheng 		return err;
4473a6ba7dcSVincent Cheng 
4483a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
4493a6ba7dcSVincent Cheng 	if (err)
4503a6ba7dcSVincent Cheng 		return err;
4513a6ba7dcSVincent Cheng 
4523a6ba7dcSVincent Cheng 	/* Set sync trigger mask */
4533a6ba7dcSVincent Cheng 	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
4543a6ba7dcSVincent Cheng 
4553a6ba7dcSVincent Cheng 	if (qn)
4563a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
4573a6ba7dcSVincent Cheng 
4583a6ba7dcSVincent Cheng 	if (qn_plus_1)
4593a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
4603a6ba7dcSVincent Cheng 
4613a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
4623a6ba7dcSVincent Cheng 	if (err)
4633a6ba7dcSVincent Cheng 		return err;
4643a6ba7dcSVincent Cheng 
4657ea5fda2SMin Li 	/* PLL5 can have OUT8 as second additional output. */
4667ea5fda2SMin Li 	if ((pll == 5) && (qn_plus_1 != 0)) {
4677ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
4687ea5fda2SMin Li 				 &temp, sizeof(temp));
4697ea5fda2SMin Li 		if (err)
4707ea5fda2SMin Li 			return err;
4717ea5fda2SMin Li 
4727ea5fda2SMin Li 		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
4737ea5fda2SMin Li 
4747ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
4757ea5fda2SMin Li 				  &temp, sizeof(temp));
4767ea5fda2SMin Li 		if (err)
4777ea5fda2SMin Li 			return err;
4787ea5fda2SMin Li 
4797ea5fda2SMin Li 		temp |= Q9_TO_Q8_SYNC_TRIG;
4807ea5fda2SMin Li 
4817ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
4827ea5fda2SMin Li 				  &temp, sizeof(temp));
4837ea5fda2SMin Li 		if (err)
4847ea5fda2SMin Li 			return err;
4857ea5fda2SMin Li 	}
4867ea5fda2SMin Li 
4877ea5fda2SMin Li 	/* PLL6 can have OUT11 as second additional output. */
4887ea5fda2SMin Li 	if ((pll == 6) && (qn_plus_1 != 0)) {
4897ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
4907ea5fda2SMin Li 				 &temp, sizeof(temp));
4917ea5fda2SMin Li 		if (err)
4927ea5fda2SMin Li 			return err;
4937ea5fda2SMin Li 
4947ea5fda2SMin Li 		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
4957ea5fda2SMin Li 
4967ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
4977ea5fda2SMin Li 				  &temp, sizeof(temp));
4987ea5fda2SMin Li 		if (err)
4997ea5fda2SMin Li 			return err;
5007ea5fda2SMin Li 
5017ea5fda2SMin Li 		temp |= Q10_TO_Q11_SYNC_TRIG;
5027ea5fda2SMin Li 
5037ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
5047ea5fda2SMin Li 				  &temp, sizeof(temp));
5057ea5fda2SMin Li 		if (err)
5067ea5fda2SMin Li 			return err;
5077ea5fda2SMin Li 	}
5087ea5fda2SMin Li 
5093a6ba7dcSVincent Cheng 	/* Place master sync out of reset */
5103a6ba7dcSVincent Cheng 	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
5113a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5123a6ba7dcSVincent Cheng 
5133a6ba7dcSVincent Cheng 	return err;
5143a6ba7dcSVincent Cheng }
5153a6ba7dcSVincent Cheng 
5167ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
5177ea5fda2SMin Li {
5187ea5fda2SMin Li 	int err = 0;
5197ea5fda2SMin Li 
5207ea5fda2SMin Li 	switch (tod_addr) {
5217ea5fda2SMin Li 	case TOD_0:
5227ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
5237ea5fda2SMin Li 		break;
5247ea5fda2SMin Li 	case TOD_1:
5257ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
5267ea5fda2SMin Li 		break;
5277ea5fda2SMin Li 	case TOD_2:
5287ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
5297ea5fda2SMin Li 		break;
5307ea5fda2SMin Li 	case TOD_3:
5317ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
5327ea5fda2SMin Li 		break;
5337ea5fda2SMin Li 	default:
5347ea5fda2SMin Li 		err = -EINVAL;
5357ea5fda2SMin Li 	}
5367ea5fda2SMin Li 
5377ea5fda2SMin Li 	return err;
5387ea5fda2SMin Li }
5397ea5fda2SMin Li 
5403a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel)
5413a6ba7dcSVincent Cheng {
5423a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
5433a6ba7dcSVincent Cheng 
5443a6ba7dcSVincent Cheng 	u8 pll;
5453a6ba7dcSVincent Cheng 	u8 sync_src;
5463a6ba7dcSVincent Cheng 	u8 qn;
5473a6ba7dcSVincent Cheng 	u8 qn_plus_1;
5483a6ba7dcSVincent Cheng 	int err = 0;
5497ea5fda2SMin Li 	u8 out8_mux = 0;
5507ea5fda2SMin Li 	u8 out11_mux = 0;
5517ea5fda2SMin Li 	u8 temp;
5523a6ba7dcSVincent Cheng 
5533a6ba7dcSVincent Cheng 	u16 output_mask = channel->output_mask;
5543a6ba7dcSVincent Cheng 
5557ea5fda2SMin Li 	err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
5567ea5fda2SMin Li 	if (err)
5577ea5fda2SMin Li 		return err;
5587ea5fda2SMin Li 
5597ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
5607ea5fda2SMin Li 			 &temp, sizeof(temp));
5617ea5fda2SMin Li 	if (err)
5627ea5fda2SMin Li 		return err;
5637ea5fda2SMin Li 
5647ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
5657ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
5667ea5fda2SMin Li 		out8_mux = 1;
5677ea5fda2SMin Li 
5687ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
5697ea5fda2SMin Li 			 &temp, sizeof(temp));
5707ea5fda2SMin Li 	if (err)
5717ea5fda2SMin Li 		return err;
5727ea5fda2SMin Li 
5737ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
5747ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
5757ea5fda2SMin Li 		out11_mux = 1;
5763a6ba7dcSVincent Cheng 
5773a6ba7dcSVincent Cheng 	for (pll = 0; pll < 8; pll++) {
5787ea5fda2SMin Li 		qn = 0;
5797ea5fda2SMin Li 		qn_plus_1 = 0;
5803a6ba7dcSVincent Cheng 
5813a6ba7dcSVincent Cheng 		if (pll < 4) {
5823a6ba7dcSVincent Cheng 			/* First 4 pll has 2 outputs */
5837ea5fda2SMin Li 			qn = output_mask & 0x1;
5847ea5fda2SMin Li 			output_mask = output_mask >> 1;
5853a6ba7dcSVincent Cheng 			qn_plus_1 = output_mask & 0x1;
5863a6ba7dcSVincent Cheng 			output_mask = output_mask >> 1;
5877ea5fda2SMin Li 		} else if (pll == 4) {
5887ea5fda2SMin Li 			if (out8_mux == 0) {
5897ea5fda2SMin Li 				qn = output_mask & 0x1;
5907ea5fda2SMin Li 				output_mask = output_mask >> 1;
5917ea5fda2SMin Li 			}
5927ea5fda2SMin Li 		} else if (pll == 5) {
5937ea5fda2SMin Li 			if (out8_mux) {
5947ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
5957ea5fda2SMin Li 				output_mask = output_mask >> 1;
5967ea5fda2SMin Li 			}
5977ea5fda2SMin Li 			qn = output_mask & 0x1;
5987ea5fda2SMin Li 			output_mask = output_mask >> 1;
5997ea5fda2SMin Li 		} else if (pll == 6) {
6007ea5fda2SMin Li 			qn = output_mask & 0x1;
6017ea5fda2SMin Li 			output_mask = output_mask >> 1;
6027ea5fda2SMin Li 			if (out11_mux) {
6037ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6047ea5fda2SMin Li 				output_mask = output_mask >> 1;
6057ea5fda2SMin Li 			}
6067ea5fda2SMin Li 		} else if (pll == 7) {
6077ea5fda2SMin Li 			if (out11_mux == 0) {
6087ea5fda2SMin Li 				qn = output_mask & 0x1;
6097ea5fda2SMin Li 				output_mask = output_mask >> 1;
6107ea5fda2SMin Li 			}
6113a6ba7dcSVincent Cheng 		}
6123a6ba7dcSVincent Cheng 
6133a6ba7dcSVincent Cheng 		if ((qn != 0) || (qn_plus_1 != 0))
6143a6ba7dcSVincent Cheng 			err = _sync_pll_output(idtcm, pll, sync_src, qn,
6153a6ba7dcSVincent Cheng 					       qn_plus_1);
6163a6ba7dcSVincent Cheng 
6173a6ba7dcSVincent Cheng 		if (err)
6183a6ba7dcSVincent Cheng 			return err;
6193a6ba7dcSVincent Cheng 	}
6203a6ba7dcSVincent Cheng 
6213a6ba7dcSVincent Cheng 	return err;
6223a6ba7dcSVincent Cheng }
6233a6ba7dcSVincent Cheng 
6247ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
6253a6ba7dcSVincent Cheng 			       struct timespec64 const *ts,
6263a6ba7dcSVincent Cheng 			       enum hw_tod_write_trig_sel wr_trig)
6273a6ba7dcSVincent Cheng {
6283a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
6293a6ba7dcSVincent Cheng 
6303a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
6313a6ba7dcSVincent Cheng 	u8 cmd;
6323a6ba7dcSVincent Cheng 	int err;
6333a6ba7dcSVincent Cheng 	struct timespec64 local_ts = *ts;
6343a6ba7dcSVincent Cheng 	s64 total_overhead_ns;
6353a6ba7dcSVincent Cheng 
6363a6ba7dcSVincent Cheng 	/* Configure HW TOD write trigger. */
6373a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6383a6ba7dcSVincent Cheng 			 &cmd, sizeof(cmd));
6393a6ba7dcSVincent Cheng 
6403a6ba7dcSVincent Cheng 	if (err)
6413a6ba7dcSVincent Cheng 		return err;
6423a6ba7dcSVincent Cheng 
6433a6ba7dcSVincent Cheng 	cmd &= ~(0x0f);
6443a6ba7dcSVincent Cheng 	cmd |= wr_trig | 0x08;
6453a6ba7dcSVincent Cheng 
6463a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6473a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
6483a6ba7dcSVincent Cheng 
6493a6ba7dcSVincent Cheng 	if (err)
6503a6ba7dcSVincent Cheng 		return err;
6513a6ba7dcSVincent Cheng 
6523a6ba7dcSVincent Cheng 	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
6533a6ba7dcSVincent Cheng 
6543a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6553a6ba7dcSVincent Cheng 
6563a6ba7dcSVincent Cheng 		if (err)
6573a6ba7dcSVincent Cheng 			return err;
6583a6ba7dcSVincent Cheng 
6593a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
6603a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
6613a6ba7dcSVincent Cheng 
6623a6ba7dcSVincent Cheng 		if (err)
6633a6ba7dcSVincent Cheng 			return err;
6643a6ba7dcSVincent Cheng 	}
6653a6ba7dcSVincent Cheng 
6663a6ba7dcSVincent Cheng 	/* ARM HW TOD write trigger. */
6673a6ba7dcSVincent Cheng 	cmd &= ~(0x08);
6683a6ba7dcSVincent Cheng 
6693a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6703a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
6713a6ba7dcSVincent Cheng 
6723a6ba7dcSVincent Cheng 	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
6733a6ba7dcSVincent Cheng 
6743a6ba7dcSVincent Cheng 		if (idtcm->calculate_overhead_flag) {
6751ece2fbeSVincent Cheng 			/* Assumption: I2C @ 400KHz */
6767260d1c8SMin Li 			ktime_t diff = ktime_sub(ktime_get_raw(),
6777260d1c8SMin Li 						 idtcm->start_time);
6787260d1c8SMin Li 			total_overhead_ns =  ktime_to_ns(diff)
6793a6ba7dcSVincent Cheng 					     + idtcm->tod_write_overhead_ns
6803a6ba7dcSVincent Cheng 					     + SETTIME_CORRECTION;
6813a6ba7dcSVincent Cheng 
6823a6ba7dcSVincent Cheng 			timespec64_add_ns(&local_ts, total_overhead_ns);
6833a6ba7dcSVincent Cheng 
6843a6ba7dcSVincent Cheng 			idtcm->calculate_overhead_flag = 0;
6853a6ba7dcSVincent Cheng 		}
6863a6ba7dcSVincent Cheng 
6873a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6883a6ba7dcSVincent Cheng 
6893a6ba7dcSVincent Cheng 		if (err)
6903a6ba7dcSVincent Cheng 			return err;
6913a6ba7dcSVincent Cheng 
6923a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
6933a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
6943a6ba7dcSVincent Cheng 	}
6953a6ba7dcSVincent Cheng 
6963a6ba7dcSVincent Cheng 	return err;
6973a6ba7dcSVincent Cheng }
6983a6ba7dcSVincent Cheng 
6997ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
7007ea5fda2SMin Li 				    struct timespec64 const *ts,
7017ea5fda2SMin Li 				    enum scsr_tod_write_trig_sel wr_trig,
7027ea5fda2SMin Li 				    enum scsr_tod_write_type_sel wr_type)
7037ea5fda2SMin Li {
7047ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
7057ea5fda2SMin Li 	unsigned char buf[TOD_BYTE_COUNT], cmd;
7067ea5fda2SMin Li 	struct timespec64 local_ts = *ts;
7077ea5fda2SMin Li 	int err, count = 0;
7087ea5fda2SMin Li 
7097ea5fda2SMin Li 	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
7107ea5fda2SMin Li 
7117ea5fda2SMin Li 	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7127ea5fda2SMin Li 
7137ea5fda2SMin Li 	if (err)
7147ea5fda2SMin Li 		return err;
7157ea5fda2SMin Li 
7167ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
7177ea5fda2SMin Li 			  buf, sizeof(buf));
7187ea5fda2SMin Li 	if (err)
7197ea5fda2SMin Li 		return err;
7207ea5fda2SMin Li 
7217ea5fda2SMin Li 	/* Trigger the write operation. */
7227ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7237ea5fda2SMin Li 			 &cmd, sizeof(cmd));
7247ea5fda2SMin Li 	if (err)
7257ea5fda2SMin Li 		return err;
7267ea5fda2SMin Li 
7277ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
7287ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
7297ea5fda2SMin Li 	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
7307ea5fda2SMin Li 	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
7317ea5fda2SMin Li 
7327ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
7337ea5fda2SMin Li 			   &cmd, sizeof(cmd));
7347ea5fda2SMin Li 	if (err)
7357ea5fda2SMin Li 		return err;
7367ea5fda2SMin Li 
7377ea5fda2SMin Li 	/* Wait for the operation to complete. */
7387ea5fda2SMin Li 	while (1) {
7397ea5fda2SMin Li 		/* pps trigger takes up to 1 sec to complete */
7407ea5fda2SMin Li 		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
7417ea5fda2SMin Li 			msleep(50);
7427ea5fda2SMin Li 
7437ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7447ea5fda2SMin Li 				 &cmd, sizeof(cmd));
7457ea5fda2SMin Li 		if (err)
7467ea5fda2SMin Li 			return err;
7477ea5fda2SMin Li 
748251f4fe2SMin Li 		if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
7497ea5fda2SMin Li 			break;
7507ea5fda2SMin Li 
7517ea5fda2SMin Li 		if (++count > 20) {
7527ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
7537ea5fda2SMin Li 				"Timed out waiting for the write counter\n");
7547ea5fda2SMin Li 			return -EIO;
7557ea5fda2SMin Li 		}
7567ea5fda2SMin Li 	}
7577ea5fda2SMin Li 
7587ea5fda2SMin Li 	return 0;
7597ea5fda2SMin Li }
7607ea5fda2SMin Li 
7617260d1c8SMin Li static int get_output_base_addr(u8 outn)
7627260d1c8SMin Li {
7637260d1c8SMin Li 	int base;
7647260d1c8SMin Li 
7657260d1c8SMin Li 	switch (outn) {
7667260d1c8SMin Li 	case 0:
7677260d1c8SMin Li 		base = OUTPUT_0;
7687260d1c8SMin Li 		break;
7697260d1c8SMin Li 	case 1:
7707260d1c8SMin Li 		base = OUTPUT_1;
7717260d1c8SMin Li 		break;
7727260d1c8SMin Li 	case 2:
7737260d1c8SMin Li 		base = OUTPUT_2;
7747260d1c8SMin Li 		break;
7757260d1c8SMin Li 	case 3:
7767260d1c8SMin Li 		base = OUTPUT_3;
7777260d1c8SMin Li 		break;
7787260d1c8SMin Li 	case 4:
7797260d1c8SMin Li 		base = OUTPUT_4;
7807260d1c8SMin Li 		break;
7817260d1c8SMin Li 	case 5:
7827260d1c8SMin Li 		base = OUTPUT_5;
7837260d1c8SMin Li 		break;
7847260d1c8SMin Li 	case 6:
7857260d1c8SMin Li 		base = OUTPUT_6;
7867260d1c8SMin Li 		break;
7877260d1c8SMin Li 	case 7:
7887260d1c8SMin Li 		base = OUTPUT_7;
7897260d1c8SMin Li 		break;
7907260d1c8SMin Li 	case 8:
7917260d1c8SMin Li 		base = OUTPUT_8;
7927260d1c8SMin Li 		break;
7937260d1c8SMin Li 	case 9:
7947260d1c8SMin Li 		base = OUTPUT_9;
7957260d1c8SMin Li 		break;
7967260d1c8SMin Li 	case 10:
7977260d1c8SMin Li 		base = OUTPUT_10;
7987260d1c8SMin Li 		break;
7997260d1c8SMin Li 	case 11:
8007260d1c8SMin Li 		base = OUTPUT_11;
8017260d1c8SMin Li 		break;
8027260d1c8SMin Li 	default:
8037260d1c8SMin Li 		base = -EINVAL;
8047260d1c8SMin Li 	}
8057260d1c8SMin Li 
8067260d1c8SMin Li 	return base;
8077260d1c8SMin Li }
8087260d1c8SMin Li 
809*da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
810251f4fe2SMin Li 				     struct timespec64 const *ts)
8113a6ba7dcSVincent Cheng {
8123a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8133a6ba7dcSVincent Cheng 	int err;
8143a6ba7dcSVincent Cheng 
815251f4fe2SMin Li 	err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
8163a6ba7dcSVincent Cheng 
8177ea5fda2SMin Li 	if (err) {
8187ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
819251f4fe2SMin Li 			"%s: Set HW ToD failed\n", __func__);
8203a6ba7dcSVincent Cheng 		return err;
8217ea5fda2SMin Li 	}
8223a6ba7dcSVincent Cheng 
8237ea5fda2SMin Li 	return idtcm_sync_pps_output(channel);
8247ea5fda2SMin Li }
8253a6ba7dcSVincent Cheng 
826*da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel,
8277ea5fda2SMin Li 			  struct timespec64 const *ts,
8287ea5fda2SMin Li 			  enum scsr_tod_write_type_sel wr_type)
8297ea5fda2SMin Li {
8307ea5fda2SMin Li 	return _idtcm_set_dpll_scsr_tod(channel, ts,
8317ea5fda2SMin Li 					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
8327ea5fda2SMin Li 					wr_type);
8333a6ba7dcSVincent Cheng }
8343a6ba7dcSVincent Cheng 
8353a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
8363a6ba7dcSVincent Cheng 					  s32 offset_ns)
8373a6ba7dcSVincent Cheng {
8383a6ba7dcSVincent Cheng 	int err;
8393a6ba7dcSVincent Cheng 	int i;
8403a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8413a6ba7dcSVincent Cheng 
8423a6ba7dcSVincent Cheng 	u8 buf[4];
8433a6ba7dcSVincent Cheng 
8443a6ba7dcSVincent Cheng 	for (i = 0; i < 4; i++) {
8453a6ba7dcSVincent Cheng 		buf[i] = 0xff & (offset_ns);
8463a6ba7dcSVincent Cheng 		offset_ns >>= 8;
8473a6ba7dcSVincent Cheng 	}
8483a6ba7dcSVincent Cheng 
8493a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
8503a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
8513a6ba7dcSVincent Cheng 
8523a6ba7dcSVincent Cheng 	return err;
8533a6ba7dcSVincent Cheng }
8543a6ba7dcSVincent Cheng 
8553a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
8563a6ba7dcSVincent Cheng 					       u32 max_ffo_ppb)
8573a6ba7dcSVincent Cheng {
8583a6ba7dcSVincent Cheng 	int err;
8593a6ba7dcSVincent Cheng 	u8 i;
8603a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8613a6ba7dcSVincent Cheng 
8623a6ba7dcSVincent Cheng 	u8 buf[3];
8633a6ba7dcSVincent Cheng 
8643a6ba7dcSVincent Cheng 	if (max_ffo_ppb & 0xff000000)
8653a6ba7dcSVincent Cheng 		max_ffo_ppb = 0;
8663a6ba7dcSVincent Cheng 
8673a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
8683a6ba7dcSVincent Cheng 		buf[i] = 0xff & (max_ffo_ppb);
8693a6ba7dcSVincent Cheng 		max_ffo_ppb >>= 8;
8703a6ba7dcSVincent Cheng 	}
8713a6ba7dcSVincent Cheng 
8723a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
8733a6ba7dcSVincent Cheng 			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
8743a6ba7dcSVincent Cheng 
8753a6ba7dcSVincent Cheng 	return err;
8763a6ba7dcSVincent Cheng }
8773a6ba7dcSVincent Cheng 
8783a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
8793a6ba7dcSVincent Cheng {
8803a6ba7dcSVincent Cheng 	int err;
8813a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8823a6ba7dcSVincent Cheng 
8833a6ba7dcSVincent Cheng 	u8 buf;
8843a6ba7dcSVincent Cheng 
8853a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
8863a6ba7dcSVincent Cheng 			 &buf, sizeof(buf));
8873a6ba7dcSVincent Cheng 
8883a6ba7dcSVincent Cheng 	if (err)
8893a6ba7dcSVincent Cheng 		return err;
8903a6ba7dcSVincent Cheng 
8913a6ba7dcSVincent Cheng 	if (buf == 0) {
8923a6ba7dcSVincent Cheng 		buf = 0x01;
8933a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
8943a6ba7dcSVincent Cheng 				  PULL_IN_CTRL, &buf, sizeof(buf));
8953a6ba7dcSVincent Cheng 	} else {
8963a6ba7dcSVincent Cheng 		err = -EBUSY;
8973a6ba7dcSVincent Cheng 	}
8983a6ba7dcSVincent Cheng 
8993a6ba7dcSVincent Cheng 	return err;
9003a6ba7dcSVincent Cheng }
9013a6ba7dcSVincent Cheng 
9023a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
9033a6ba7dcSVincent Cheng 				  s32 offset_ns,
9043a6ba7dcSVincent Cheng 				  u32 max_ffo_ppb)
9053a6ba7dcSVincent Cheng {
9063a6ba7dcSVincent Cheng 	int err;
9073a6ba7dcSVincent Cheng 
9083a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
9093a6ba7dcSVincent Cheng 
9103a6ba7dcSVincent Cheng 	if (err)
9113a6ba7dcSVincent Cheng 		return err;
9123a6ba7dcSVincent Cheng 
9133a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
9143a6ba7dcSVincent Cheng 
9153a6ba7dcSVincent Cheng 	if (err)
9163a6ba7dcSVincent Cheng 		return err;
9173a6ba7dcSVincent Cheng 
9183a6ba7dcSVincent Cheng 	err = idtcm_start_phase_pull_in(channel);
9193a6ba7dcSVincent Cheng 
9203a6ba7dcSVincent Cheng 	return err;
9213a6ba7dcSVincent Cheng }
9223a6ba7dcSVincent Cheng 
9237ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel)
9247ea5fda2SMin Li {
9257ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
9267ea5fda2SMin Li 	s64 current_ns = 0;
9277ea5fda2SMin Li 	s64 lowest_ns = 0;
9287ea5fda2SMin Li 	int err;
9297ea5fda2SMin Li 	u8 i;
9307ea5fda2SMin Li 
9317ea5fda2SMin Li 	ktime_t start;
9327ea5fda2SMin Li 	ktime_t stop;
9337260d1c8SMin Li 	ktime_t diff;
9347ea5fda2SMin Li 
9357ea5fda2SMin Li 	char buf[TOD_BYTE_COUNT] = {0};
9367ea5fda2SMin Li 
9377ea5fda2SMin Li 	/* Set page offset */
9387ea5fda2SMin Li 	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
9397ea5fda2SMin Li 		    buf, sizeof(buf));
9407ea5fda2SMin Li 
9417ea5fda2SMin Li 	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
9427ea5fda2SMin Li 
9437ea5fda2SMin Li 		start = ktime_get_raw();
9447ea5fda2SMin Li 
9457ea5fda2SMin Li 		err = idtcm_write(idtcm, channel->hw_dpll_n,
9467ea5fda2SMin Li 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
9477ea5fda2SMin Li 
9487ea5fda2SMin Li 		if (err)
9497ea5fda2SMin Li 			return err;
9507ea5fda2SMin Li 
9517ea5fda2SMin Li 		stop = ktime_get_raw();
9527ea5fda2SMin Li 
9537260d1c8SMin Li 		diff = ktime_sub(stop, start);
9547260d1c8SMin Li 
9557260d1c8SMin Li 		current_ns = ktime_to_ns(diff);
9567ea5fda2SMin Li 
9577ea5fda2SMin Li 		if (i == 0) {
9587ea5fda2SMin Li 			lowest_ns = current_ns;
9597ea5fda2SMin Li 		} else {
9607ea5fda2SMin Li 			if (current_ns < lowest_ns)
9617ea5fda2SMin Li 				lowest_ns = current_ns;
9627ea5fda2SMin Li 		}
9637ea5fda2SMin Li 	}
9647ea5fda2SMin Li 
9657ea5fda2SMin Li 	idtcm->tod_write_overhead_ns = lowest_ns;
9667ea5fda2SMin Li 
9677ea5fda2SMin Li 	return err;
9687ea5fda2SMin Li }
9697ea5fda2SMin Li 
970*da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
9713a6ba7dcSVincent Cheng {
9723a6ba7dcSVincent Cheng 	int err;
9733a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9743a6ba7dcSVincent Cheng 	struct timespec64 ts;
9753a6ba7dcSVincent Cheng 	s64 now;
9763a6ba7dcSVincent Cheng 
977*da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
9783a6ba7dcSVincent Cheng 		err = idtcm_do_phase_pull_in(channel, delta, 0);
9793a6ba7dcSVincent Cheng 	} else {
9803a6ba7dcSVincent Cheng 		idtcm->calculate_overhead_flag = 1;
9813a6ba7dcSVincent Cheng 
9827ea5fda2SMin Li 		err = set_tod_write_overhead(channel);
9837ea5fda2SMin Li 
9847ea5fda2SMin Li 		if (err)
9857ea5fda2SMin Li 			return err;
9867ea5fda2SMin Li 
9873a6ba7dcSVincent Cheng 		err = _idtcm_gettime(channel, &ts);
9883a6ba7dcSVincent Cheng 
9893a6ba7dcSVincent Cheng 		if (err)
9903a6ba7dcSVincent Cheng 			return err;
9913a6ba7dcSVincent Cheng 
9923a6ba7dcSVincent Cheng 		now = timespec64_to_ns(&ts);
9933a6ba7dcSVincent Cheng 		now += delta;
9943a6ba7dcSVincent Cheng 
9953a6ba7dcSVincent Cheng 		ts = ns_to_timespec64(now);
9963a6ba7dcSVincent Cheng 
997*da948233SMin Li 		err = _idtcm_settime_deprecated(channel, &ts);
9983a6ba7dcSVincent Cheng 	}
9993a6ba7dcSVincent Cheng 
10003a6ba7dcSVincent Cheng 	return err;
10013a6ba7dcSVincent Cheng }
10023a6ba7dcSVincent Cheng 
10033a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm)
10043a6ba7dcSVincent Cheng {
10053a6ba7dcSVincent Cheng 	u8 byte = SM_RESET_CMD;
1006251f4fe2SMin Li 	u32 status = 0;
1007251f4fe2SMin Li 	int err;
1008251f4fe2SMin Li 	u8 i;
1009251f4fe2SMin Li 
1010251f4fe2SMin Li 	clear_boot_status(idtcm);
10113a6ba7dcSVincent Cheng 
10123a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
10133a6ba7dcSVincent Cheng 
1014251f4fe2SMin Li 	if (!err) {
1015251f4fe2SMin Li 		for (i = 0; i < 30; i++) {
1016251f4fe2SMin Li 			msleep_interruptible(100);
1017251f4fe2SMin Li 			read_boot_status(idtcm, &status);
1018251f4fe2SMin Li 
1019251f4fe2SMin Li 			if (status == 0xA0) {
1020251f4fe2SMin Li 				dev_dbg(&idtcm->client->dev,
1021251f4fe2SMin Li 					"SM_RESET completed in %d ms\n",
1022251f4fe2SMin Li 					i * 100);
1023251f4fe2SMin Li 				break;
1024251f4fe2SMin Li 			}
1025251f4fe2SMin Li 		}
1026251f4fe2SMin Li 
1027251f4fe2SMin Li 		if (!status)
1028251f4fe2SMin Li 			dev_err(&idtcm->client->dev, "Timed out waiting for CM_RESET to complete\n");
1029251f4fe2SMin Li 	}
10303a6ba7dcSVincent Cheng 
10313a6ba7dcSVincent Cheng 	return err;
10323a6ba7dcSVincent Cheng }
10333a6ba7dcSVincent Cheng 
10343a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
10353a6ba7dcSVincent Cheng {
10361ece2fbeSVincent Cheng 	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
10373a6ba7dcSVincent Cheng }
10383a6ba7dcSVincent Cheng 
10393a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
10403a6ba7dcSVincent Cheng {
10413a6ba7dcSVincent Cheng 	int err;
10423a6ba7dcSVincent Cheng 	u8 buf[2] = {0};
10433a6ba7dcSVincent Cheng 
10443a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
10453a6ba7dcSVincent Cheng 
10463a6ba7dcSVincent Cheng 	*product_id = (buf[1] << 8) | buf[0];
10473a6ba7dcSVincent Cheng 
10483a6ba7dcSVincent Cheng 	return err;
10493a6ba7dcSVincent Cheng }
10503a6ba7dcSVincent Cheng 
10513a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
10523a6ba7dcSVincent Cheng {
10533a6ba7dcSVincent Cheng 	int err;
10543a6ba7dcSVincent Cheng 	u8 buf = 0;
10553a6ba7dcSVincent Cheng 
10563a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
10573a6ba7dcSVincent Cheng 
10583a6ba7dcSVincent Cheng 	*major = buf >> 1;
10593a6ba7dcSVincent Cheng 
10603a6ba7dcSVincent Cheng 	return err;
10613a6ba7dcSVincent Cheng }
10623a6ba7dcSVincent Cheng 
10633a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
10643a6ba7dcSVincent Cheng {
10653a6ba7dcSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
10663a6ba7dcSVincent Cheng }
10673a6ba7dcSVincent Cheng 
10683a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
10693a6ba7dcSVincent Cheng {
10703a6ba7dcSVincent Cheng 	return idtcm_read(idtcm,
10713a6ba7dcSVincent Cheng 			  GENERAL_STATUS,
10723a6ba7dcSVincent Cheng 			  HOTFIX_REL,
10733a6ba7dcSVincent Cheng 			  hotfix,
10743a6ba7dcSVincent Cheng 			  sizeof(u8));
10753a6ba7dcSVincent Cheng }
10763a6ba7dcSVincent Cheng 
10771ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
10781ece2fbeSVincent Cheng 					     u8 *config_select)
10793a6ba7dcSVincent Cheng {
10801ece2fbeSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
10811ece2fbeSVincent Cheng 			  config_select, sizeof(u8));
10823a6ba7dcSVincent Cheng }
10833a6ba7dcSVincent Cheng 
10843a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
10853a6ba7dcSVincent Cheng {
10863a6ba7dcSVincent Cheng 	int err = 0;
10873a6ba7dcSVincent Cheng 
10883a6ba7dcSVincent Cheng 	switch (addr) {
10897ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR:
10903a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[0].output_mask, val);
10913a6ba7dcSVincent Cheng 		break;
10927ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
10933a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[0].output_mask, val);
10943a6ba7dcSVincent Cheng 		break;
10957ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR:
10963a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[1].output_mask, val);
10973a6ba7dcSVincent Cheng 		break;
10987ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
10993a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[1].output_mask, val);
11003a6ba7dcSVincent Cheng 		break;
11017ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR:
11023a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[2].output_mask, val);
11033a6ba7dcSVincent Cheng 		break;
11047ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
11053a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[2].output_mask, val);
11063a6ba7dcSVincent Cheng 		break;
11077ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR:
11083a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[3].output_mask, val);
11093a6ba7dcSVincent Cheng 		break;
11107ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
11113a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[3].output_mask, val);
11123a6ba7dcSVincent Cheng 		break;
11133a6ba7dcSVincent Cheng 	default:
11147ea5fda2SMin Li 		err = -EFAULT; /* Bad address */;
11153a6ba7dcSVincent Cheng 		break;
11163a6ba7dcSVincent Cheng 	}
11173a6ba7dcSVincent Cheng 
11183a6ba7dcSVincent Cheng 	return err;
11193a6ba7dcSVincent Cheng }
11203a6ba7dcSVincent Cheng 
11217ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
11227ea5fda2SMin Li {
11237ea5fda2SMin Li 	if (index >= MAX_TOD) {
11247ea5fda2SMin Li 		dev_err(&idtcm->client->dev, "ToD%d not supported\n", index);
11257ea5fda2SMin Li 		return -EINVAL;
11267ea5fda2SMin Li 	}
11277ea5fda2SMin Li 
11287ea5fda2SMin Li 	if (pll >= MAX_PLL) {
11297ea5fda2SMin Li 		dev_err(&idtcm->client->dev, "Pll%d not supported\n", pll);
11307ea5fda2SMin Li 		return -EINVAL;
11317ea5fda2SMin Li 	}
11327ea5fda2SMin Li 
11337ea5fda2SMin Li 	idtcm->channel[index].pll = pll;
11347ea5fda2SMin Li 
11357ea5fda2SMin Li 	return 0;
11367ea5fda2SMin Li }
11377ea5fda2SMin Li 
11383a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm,
11393a6ba7dcSVincent Cheng 			       u16 regaddr,
11403a6ba7dcSVincent Cheng 			       u8 val)
11413a6ba7dcSVincent Cheng {
11423a6ba7dcSVincent Cheng 	int err = 0;
11433a6ba7dcSVincent Cheng 
11447ea5fda2SMin Li 	switch (regaddr) {
11457ea5fda2SMin Li 	case TOD_MASK_ADDR:
11467ea5fda2SMin Li 		if ((val & 0xf0) || !(val & 0x0f)) {
11477ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
11487ea5fda2SMin Li 				"Invalid TOD mask 0x%hhx\n", val);
11497ea5fda2SMin Li 			err = -EINVAL;
11507ea5fda2SMin Li 		} else {
11517ea5fda2SMin Li 			idtcm->tod_mask = val;
11527ea5fda2SMin Li 		}
11537ea5fda2SMin Li 		break;
11547ea5fda2SMin Li 	case TOD0_PTP_PLL_ADDR:
11557ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 0, val);
11567ea5fda2SMin Li 		break;
11577ea5fda2SMin Li 	case TOD1_PTP_PLL_ADDR:
11587ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 1, val);
11597ea5fda2SMin Li 		break;
11607ea5fda2SMin Li 	case TOD2_PTP_PLL_ADDR:
11617ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 2, val);
11627ea5fda2SMin Li 		break;
11637ea5fda2SMin Li 	case TOD3_PTP_PLL_ADDR:
11647ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 3, val);
11657ea5fda2SMin Li 		break;
11667ea5fda2SMin Li 	default:
11677ea5fda2SMin Li 		err = set_pll_output_mask(idtcm, regaddr, val);
11687ea5fda2SMin Li 		break;
11693a6ba7dcSVincent Cheng 	}
11703a6ba7dcSVincent Cheng 
11713a6ba7dcSVincent Cheng 	return err;
11723a6ba7dcSVincent Cheng }
11733a6ba7dcSVincent Cheng 
11747ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm)
11753a6ba7dcSVincent Cheng {
11763a6ba7dcSVincent Cheng 	u8 i;
11773a6ba7dcSVincent Cheng 	u8 mask;
11783a6ba7dcSVincent Cheng 
11797ea5fda2SMin Li 	dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x\n", idtcm->tod_mask);
11803a6ba7dcSVincent Cheng 
11817ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
11823a6ba7dcSVincent Cheng 		mask = 1 << i;
11833a6ba7dcSVincent Cheng 
11847ea5fda2SMin Li 		if (mask & idtcm->tod_mask)
11853a6ba7dcSVincent Cheng 			dev_dbg(&idtcm->client->dev,
11867ea5fda2SMin Li 				"TOD%d pll = %d    output_mask = 0x%04x\n",
11877ea5fda2SMin Li 				i, idtcm->channel[i].pll,
11887ea5fda2SMin Li 				idtcm->channel[i].output_mask);
11893a6ba7dcSVincent Cheng 	}
11903a6ba7dcSVincent Cheng }
11913a6ba7dcSVincent Cheng 
11923a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm,
11933a6ba7dcSVincent Cheng 			       struct device *dev)
11943a6ba7dcSVincent Cheng {
11957ea5fda2SMin Li 	char fname[128] = FW_FILENAME;
11963a6ba7dcSVincent Cheng 	const struct firmware *fw;
11973a6ba7dcSVincent Cheng 	struct idtcm_fwrc *rec;
11983a6ba7dcSVincent Cheng 	u32 regaddr;
11993a6ba7dcSVincent Cheng 	int err;
12003a6ba7dcSVincent Cheng 	s32 len;
12013a6ba7dcSVincent Cheng 	u8 val;
12023a6ba7dcSVincent Cheng 	u8 loaddr;
12033a6ba7dcSVincent Cheng 
12047ea5fda2SMin Li 	if (firmware) /* module parameter */
12057ea5fda2SMin Li 		snprintf(fname, sizeof(fname), "%s", firmware);
12063a6ba7dcSVincent Cheng 
12077ea5fda2SMin Li 	dev_dbg(&idtcm->client->dev, "requesting firmware '%s'\n", fname);
12083a6ba7dcSVincent Cheng 
12097ea5fda2SMin Li 	err = request_firmware(&fw, fname, dev);
12107ea5fda2SMin Li 
12117ea5fda2SMin Li 	if (err) {
12127ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
12137ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
12147ea5fda2SMin Li 			__LINE__,
12157ea5fda2SMin Li 			__func__);
12163a6ba7dcSVincent Cheng 		return err;
12177ea5fda2SMin Li 	}
12183a6ba7dcSVincent Cheng 
12193a6ba7dcSVincent Cheng 	dev_dbg(&idtcm->client->dev, "firmware size %zu bytes\n", fw->size);
12203a6ba7dcSVincent Cheng 
12213a6ba7dcSVincent Cheng 	rec = (struct idtcm_fwrc *) fw->data;
12223a6ba7dcSVincent Cheng 
1223251f4fe2SMin Li 	if (contains_full_configuration(fw))
12243a6ba7dcSVincent Cheng 		idtcm_state_machine_reset(idtcm);
12253a6ba7dcSVincent Cheng 
12263a6ba7dcSVincent Cheng 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
12273a6ba7dcSVincent Cheng 
12283a6ba7dcSVincent Cheng 		if (rec->reserved) {
12293a6ba7dcSVincent Cheng 			dev_err(&idtcm->client->dev,
12303a6ba7dcSVincent Cheng 				"bad firmware, reserved field non-zero\n");
12313a6ba7dcSVincent Cheng 			err = -EINVAL;
12323a6ba7dcSVincent Cheng 		} else {
12333a6ba7dcSVincent Cheng 			regaddr = rec->hiaddr << 8;
12343a6ba7dcSVincent Cheng 			regaddr |= rec->loaddr;
12353a6ba7dcSVincent Cheng 
12363a6ba7dcSVincent Cheng 			val = rec->value;
12373a6ba7dcSVincent Cheng 			loaddr = rec->loaddr;
12383a6ba7dcSVincent Cheng 
12393a6ba7dcSVincent Cheng 			rec++;
12403a6ba7dcSVincent Cheng 
12413a6ba7dcSVincent Cheng 			err = check_and_set_masks(idtcm, regaddr, val);
12423a6ba7dcSVincent Cheng 		}
12433a6ba7dcSVincent Cheng 
12447ea5fda2SMin Li 		if (err != -EINVAL) {
12457ea5fda2SMin Li 			err = 0;
12467ea5fda2SMin Li 
12473a6ba7dcSVincent Cheng 			/* Top (status registers) and bottom are read-only */
12483a6ba7dcSVincent Cheng 			if ((regaddr < GPIO_USER_CONTROL)
12493a6ba7dcSVincent Cheng 			    || (regaddr >= SCRATCH))
12503a6ba7dcSVincent Cheng 				continue;
12513a6ba7dcSVincent Cheng 
12523a6ba7dcSVincent Cheng 			/* Page size 128, last 4 bytes of page skipped */
12533a6ba7dcSVincent Cheng 			if (((loaddr > 0x7b) && (loaddr <= 0x7f))
12543e14462fSYang Yingliang 			     || loaddr > 0xfb)
12553a6ba7dcSVincent Cheng 				continue;
12563a6ba7dcSVincent Cheng 
12573a6ba7dcSVincent Cheng 			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
12583a6ba7dcSVincent Cheng 		}
12593a6ba7dcSVincent Cheng 
12603a6ba7dcSVincent Cheng 		if (err)
12613a6ba7dcSVincent Cheng 			goto out;
12623a6ba7dcSVincent Cheng 	}
12633a6ba7dcSVincent Cheng 
12647ea5fda2SMin Li 	display_pll_and_masks(idtcm);
12653a6ba7dcSVincent Cheng 
12663a6ba7dcSVincent Cheng out:
12673a6ba7dcSVincent Cheng 	release_firmware(fw);
12683a6ba7dcSVincent Cheng 	return err;
12693a6ba7dcSVincent Cheng }
12703a6ba7dcSVincent Cheng 
12717ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel,
12727ea5fda2SMin Li 			       bool enable, unsigned int outn)
12733a6ba7dcSVincent Cheng {
12743a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
12757260d1c8SMin Li 	int base;
12763a6ba7dcSVincent Cheng 	int err;
12777ea5fda2SMin Li 	u8 val;
12783a6ba7dcSVincent Cheng 
12797260d1c8SMin Li 	base = get_output_base_addr(outn);
12807260d1c8SMin Li 
12817260d1c8SMin Li 	if (!(base > 0)) {
12827260d1c8SMin Li 		dev_err(&idtcm->client->dev,
12837260d1c8SMin Li 			"%s - Unsupported out%d", __func__, outn);
12847260d1c8SMin Li 		return base;
12857260d1c8SMin Li 	}
12867260d1c8SMin Li 
12877260d1c8SMin Li 	err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
12883a6ba7dcSVincent Cheng 
12893a6ba7dcSVincent Cheng 	if (err)
12903a6ba7dcSVincent Cheng 		return err;
12913a6ba7dcSVincent Cheng 
12923a6ba7dcSVincent Cheng 	if (enable)
12933a6ba7dcSVincent Cheng 		val |= SQUELCH_DISABLE;
12943a6ba7dcSVincent Cheng 	else
12953a6ba7dcSVincent Cheng 		val &= ~SQUELCH_DISABLE;
12963a6ba7dcSVincent Cheng 
12977260d1c8SMin Li 	return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
12987ea5fda2SMin Li }
12997ea5fda2SMin Li 
13007ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel,
13017ea5fda2SMin Li 				    bool enable)
13027ea5fda2SMin Li {
13037ea5fda2SMin Li 	u16 mask;
13047ea5fda2SMin Li 	int err;
13057ea5fda2SMin Li 	u8 outn;
13067ea5fda2SMin Li 
13077ea5fda2SMin Li 	mask = channel->output_mask;
13087ea5fda2SMin Li 	outn = 0;
13097ea5fda2SMin Li 
13107ea5fda2SMin Li 	while (mask) {
13117ea5fda2SMin Li 
13127ea5fda2SMin Li 		if (mask & 0x1) {
13137ea5fda2SMin Li 
13147ea5fda2SMin Li 			err = idtcm_output_enable(channel, enable, outn);
13153a6ba7dcSVincent Cheng 
13163a6ba7dcSVincent Cheng 			if (err)
13173a6ba7dcSVincent Cheng 				return err;
13187ea5fda2SMin Li 		}
13197ea5fda2SMin Li 
13207ea5fda2SMin Li 		mask >>= 0x1;
13217ea5fda2SMin Li 		outn++;
13227ea5fda2SMin Li 	}
13233a6ba7dcSVincent Cheng 
13243a6ba7dcSVincent Cheng 	return 0;
13253a6ba7dcSVincent Cheng }
13263a6ba7dcSVincent Cheng 
13277ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel,
13287ea5fda2SMin Li 			       bool enable,
13297ea5fda2SMin Li 			       struct ptp_perout_request *perout)
13307ea5fda2SMin Li {
13317ea5fda2SMin Li 	unsigned int flags = perout->flags;
13327ea5fda2SMin Li 
13337ea5fda2SMin Li 	if (flags == PEROUT_ENABLE_OUTPUT_MASK)
13347ea5fda2SMin Li 		return idtcm_output_mask_enable(channel, enable);
13357ea5fda2SMin Li 
13367ea5fda2SMin Li 	/* Enable/disable individual output instead */
13377ea5fda2SMin Li 	return idtcm_output_enable(channel, enable, perout->index);
13387ea5fda2SMin Li }
13397ea5fda2SMin Li 
13407260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel,
13417260d1c8SMin Li 			      enum pll_mode *pll_mode)
13427260d1c8SMin Li {
13437260d1c8SMin Li 	struct idtcm *idtcm = channel->idtcm;
13447260d1c8SMin Li 	int err;
13457260d1c8SMin Li 	u8 dpll_mode;
13467260d1c8SMin Li 
13477260d1c8SMin Li 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13487260d1c8SMin Li 			 &dpll_mode, sizeof(dpll_mode));
13497260d1c8SMin Li 	if (err)
13507260d1c8SMin Li 		return err;
13517260d1c8SMin Li 
13527260d1c8SMin Li 	*pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
13537260d1c8SMin Li 
13547260d1c8SMin Li 	return 0;
13557260d1c8SMin Li }
13567260d1c8SMin Li 
13573a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel,
13583a6ba7dcSVincent Cheng 			      enum pll_mode pll_mode)
13593a6ba7dcSVincent Cheng {
13603a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13613a6ba7dcSVincent Cheng 	int err;
13623a6ba7dcSVincent Cheng 	u8 dpll_mode;
13633a6ba7dcSVincent Cheng 
13643a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13653a6ba7dcSVincent Cheng 			 &dpll_mode, sizeof(dpll_mode));
13663a6ba7dcSVincent Cheng 	if (err)
13673a6ba7dcSVincent Cheng 		return err;
13683a6ba7dcSVincent Cheng 
13693a6ba7dcSVincent Cheng 	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
13703a6ba7dcSVincent Cheng 
13713a6ba7dcSVincent Cheng 	dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
13723a6ba7dcSVincent Cheng 
13733a6ba7dcSVincent Cheng 	channel->pll_mode = pll_mode;
13743a6ba7dcSVincent Cheng 
13753a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
13763a6ba7dcSVincent Cheng 			  &dpll_mode, sizeof(dpll_mode));
13773a6ba7dcSVincent Cheng 	if (err)
13783a6ba7dcSVincent Cheng 		return err;
13793a6ba7dcSVincent Cheng 
13803a6ba7dcSVincent Cheng 	return 0;
13813a6ba7dcSVincent Cheng }
13823a6ba7dcSVincent Cheng 
13833a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */
13843a6ba7dcSVincent Cheng 
1385425d2b1cSVincent Cheng /**
1386425d2b1cSVincent Cheng  * @brief Maximum absolute value for write phase offset in picoseconds
1387425d2b1cSVincent Cheng  *
1388425d2b1cSVincent Cheng  * Destination signed register is 32-bit register in resolution of 50ps
1389425d2b1cSVincent Cheng  *
1390425d2b1cSVincent Cheng  * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1391425d2b1cSVincent Cheng  */
1392425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1393425d2b1cSVincent Cheng {
1394425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1395425d2b1cSVincent Cheng 
1396425d2b1cSVincent Cheng 	int err;
1397425d2b1cSVincent Cheng 	u8 i;
1398425d2b1cSVincent Cheng 	u8 buf[4] = {0};
1399425d2b1cSVincent Cheng 	s32 phase_50ps;
1400425d2b1cSVincent Cheng 	s64 offset_ps;
1401425d2b1cSVincent Cheng 
1402425d2b1cSVincent Cheng 	if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
1403425d2b1cSVincent Cheng 
1404425d2b1cSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
1405425d2b1cSVincent Cheng 
1406425d2b1cSVincent Cheng 		if (err)
1407425d2b1cSVincent Cheng 			return err;
1408425d2b1cSVincent Cheng 	}
1409425d2b1cSVincent Cheng 
1410425d2b1cSVincent Cheng 	offset_ps = (s64)delta_ns * 1000;
1411425d2b1cSVincent Cheng 
1412425d2b1cSVincent Cheng 	/*
1413425d2b1cSVincent Cheng 	 * Check for 32-bit signed max * 50:
1414425d2b1cSVincent Cheng 	 *
1415425d2b1cSVincent Cheng 	 * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1416425d2b1cSVincent Cheng 	 */
1417425d2b1cSVincent Cheng 	if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
1418425d2b1cSVincent Cheng 		offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
1419425d2b1cSVincent Cheng 	else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
1420425d2b1cSVincent Cheng 		offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
1421425d2b1cSVincent Cheng 
14227260d1c8SMin Li 	phase_50ps = div_s64(offset_ps, 50);
1423425d2b1cSVincent Cheng 
1424425d2b1cSVincent Cheng 	for (i = 0; i < 4; i++) {
1425425d2b1cSVincent Cheng 		buf[i] = phase_50ps & 0xff;
1426425d2b1cSVincent Cheng 		phase_50ps >>= 8;
1427425d2b1cSVincent Cheng 	}
1428425d2b1cSVincent Cheng 
1429425d2b1cSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1430425d2b1cSVincent Cheng 			  buf, sizeof(buf));
1431425d2b1cSVincent Cheng 
1432425d2b1cSVincent Cheng 	return err;
1433425d2b1cSVincent Cheng }
1434425d2b1cSVincent Cheng 
14357ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
14363a6ba7dcSVincent Cheng {
14373a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
14383a6ba7dcSVincent Cheng 	u8 i;
14393a6ba7dcSVincent Cheng 	int err;
14403a6ba7dcSVincent Cheng 	u8 buf[6] = {0};
14413a6ba7dcSVincent Cheng 	s64 fcw;
14423a6ba7dcSVincent Cheng 
14433a6ba7dcSVincent Cheng 	if (channel->pll_mode  != PLL_MODE_WRITE_FREQUENCY) {
14443a6ba7dcSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
14453a6ba7dcSVincent Cheng 		if (err)
14463a6ba7dcSVincent Cheng 			return err;
14473a6ba7dcSVincent Cheng 	}
14483a6ba7dcSVincent Cheng 
14493a6ba7dcSVincent Cheng 	/*
14503a6ba7dcSVincent Cheng 	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
14513a6ba7dcSVincent Cheng 	 *
14523a6ba7dcSVincent Cheng 	 * adjfreq:
14533a6ba7dcSVincent Cheng 	 *       ppb * 10^9
14543a6ba7dcSVincent Cheng 	 * FCW = ----------
14553a6ba7dcSVincent Cheng 	 *          111
14563a6ba7dcSVincent Cheng 	 *
14573a6ba7dcSVincent Cheng 	 * adjfine:
14583a6ba7dcSVincent Cheng 	 *       ppm_16 * 5^12
14593a6ba7dcSVincent Cheng 	 * FCW = -------------
14603a6ba7dcSVincent Cheng 	 *         111 * 2^4
14613a6ba7dcSVincent Cheng 	 */
14623a6ba7dcSVincent Cheng 
14633a6ba7dcSVincent Cheng 	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
14647ea5fda2SMin Li 	fcw = scaled_ppm * 244140625ULL;
14653a6ba7dcSVincent Cheng 
14667260d1c8SMin Li 	fcw = div_s64(fcw, 1776);
14673a6ba7dcSVincent Cheng 
14683a6ba7dcSVincent Cheng 	for (i = 0; i < 6; i++) {
14693a6ba7dcSVincent Cheng 		buf[i] = fcw & 0xff;
14703a6ba7dcSVincent Cheng 		fcw >>= 8;
14713a6ba7dcSVincent Cheng 	}
14723a6ba7dcSVincent Cheng 
14733a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
14743a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
14753a6ba7dcSVincent Cheng 
14763a6ba7dcSVincent Cheng 	return err;
14773a6ba7dcSVincent Cheng }
14783a6ba7dcSVincent Cheng 
14793a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
14803a6ba7dcSVincent Cheng {
14813a6ba7dcSVincent Cheng 	struct idtcm_channel *channel =
14823a6ba7dcSVincent Cheng 		container_of(ptp, struct idtcm_channel, caps);
14833a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
14843a6ba7dcSVincent Cheng 	int err;
14853a6ba7dcSVincent Cheng 
14863a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
14873a6ba7dcSVincent Cheng 
14883a6ba7dcSVincent Cheng 	err = _idtcm_gettime(channel, ts);
14893a6ba7dcSVincent Cheng 
14907ea5fda2SMin Li 	if (err)
14917ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
14927ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
14937ea5fda2SMin Li 			__LINE__,
14947ea5fda2SMin Li 			__func__);
14957ea5fda2SMin Li 
14963a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
14973a6ba7dcSVincent Cheng 
14983a6ba7dcSVincent Cheng 	return err;
14993a6ba7dcSVincent Cheng }
15003a6ba7dcSVincent Cheng 
1501*da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
15023a6ba7dcSVincent Cheng 				    const struct timespec64 *ts)
15033a6ba7dcSVincent Cheng {
15043a6ba7dcSVincent Cheng 	struct idtcm_channel *channel =
15053a6ba7dcSVincent Cheng 		container_of(ptp, struct idtcm_channel, caps);
15063a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15073a6ba7dcSVincent Cheng 	int err;
15083a6ba7dcSVincent Cheng 
15093a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
15103a6ba7dcSVincent Cheng 
1511*da948233SMin Li 	err = _idtcm_settime_deprecated(channel, ts);
15123a6ba7dcSVincent Cheng 
15137ea5fda2SMin Li 	if (err)
15147ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15157ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
15167ea5fda2SMin Li 			__LINE__,
15177ea5fda2SMin Li 			__func__);
15187ea5fda2SMin Li 
15197ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
15207ea5fda2SMin Li 
15217ea5fda2SMin Li 	return err;
15227ea5fda2SMin Li }
15237ea5fda2SMin Li 
1524*da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp,
15257ea5fda2SMin Li 			 const struct timespec64 *ts)
15267ea5fda2SMin Li {
15277ea5fda2SMin Li 	struct idtcm_channel *channel =
15287ea5fda2SMin Li 		container_of(ptp, struct idtcm_channel, caps);
15297ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
15307ea5fda2SMin Li 	int err;
15317ea5fda2SMin Li 
15327ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
15337ea5fda2SMin Li 
1534*da948233SMin Li 	err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
1535*da948233SMin Li 
1536*da948233SMin Li 	if (err)
1537*da948233SMin Li 		dev_err(&idtcm->client->dev,
1538*da948233SMin Li 			"Failed at line %d in func %s!\n",
1539*da948233SMin Li 			__LINE__,
1540*da948233SMin Li 			__func__);
1541*da948233SMin Li 
1542*da948233SMin Li 	mutex_unlock(&idtcm->reg_lock);
1543*da948233SMin Li 
1544*da948233SMin Li 	return err;
1545*da948233SMin Li }
1546*da948233SMin Li 
1547*da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
1548*da948233SMin Li {
1549*da948233SMin Li 	struct idtcm_channel *channel =
1550*da948233SMin Li 		container_of(ptp, struct idtcm_channel, caps);
1551*da948233SMin Li 	struct idtcm *idtcm = channel->idtcm;
1552*da948233SMin Li 	int err;
1553*da948233SMin Li 
1554*da948233SMin Li 	mutex_lock(&idtcm->reg_lock);
1555*da948233SMin Li 
1556*da948233SMin Li 	err = _idtcm_adjtime_deprecated(channel, delta);
15577ea5fda2SMin Li 
15587ea5fda2SMin Li 	if (err)
15597ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15607ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
15617ea5fda2SMin Li 			__LINE__,
15627ea5fda2SMin Li 			__func__);
15637ea5fda2SMin Li 
15643a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
15653a6ba7dcSVincent Cheng 
15663a6ba7dcSVincent Cheng 	return err;
15673a6ba7dcSVincent Cheng }
15683a6ba7dcSVincent Cheng 
15693a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
15703a6ba7dcSVincent Cheng {
15713a6ba7dcSVincent Cheng 	struct idtcm_channel *channel =
15723a6ba7dcSVincent Cheng 		container_of(ptp, struct idtcm_channel, caps);
15733a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15747ea5fda2SMin Li 	struct timespec64 ts;
15757ea5fda2SMin Li 	enum scsr_tod_write_type_sel type;
15767ea5fda2SMin Li 	int err;
15777ea5fda2SMin Li 
1578*da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
15797ea5fda2SMin Li 		err = idtcm_do_phase_pull_in(channel, delta, 0);
15807ea5fda2SMin Li 		if (err)
15817ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
15827ea5fda2SMin Li 				"Failed at line %d in func %s!\n",
15837ea5fda2SMin Li 				__LINE__,
15847ea5fda2SMin Li 				__func__);
15857ea5fda2SMin Li 		return err;
15867ea5fda2SMin Li 	}
15877ea5fda2SMin Li 
15887ea5fda2SMin Li 	if (delta >= 0) {
15897ea5fda2SMin Li 		ts = ns_to_timespec64(delta);
15907ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
15917ea5fda2SMin Li 	} else {
15927ea5fda2SMin Li 		ts = ns_to_timespec64(-delta);
15937ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
15947ea5fda2SMin Li 	}
15957ea5fda2SMin Li 
15967ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
15977ea5fda2SMin Li 
1598*da948233SMin Li 	err = _idtcm_settime(channel, &ts, type);
15997ea5fda2SMin Li 
16007ea5fda2SMin Li 	if (err)
16017ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16027ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
16037ea5fda2SMin Li 			__LINE__,
16047ea5fda2SMin Li 			__func__);
16057ea5fda2SMin Li 
16063a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
16073a6ba7dcSVincent Cheng 
16083a6ba7dcSVincent Cheng 	return err;
16093a6ba7dcSVincent Cheng }
16103a6ba7dcSVincent Cheng 
1611425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1612425d2b1cSVincent Cheng {
1613425d2b1cSVincent Cheng 	struct idtcm_channel *channel =
1614425d2b1cSVincent Cheng 		container_of(ptp, struct idtcm_channel, caps);
1615425d2b1cSVincent Cheng 
1616425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1617425d2b1cSVincent Cheng 
1618425d2b1cSVincent Cheng 	int err;
1619425d2b1cSVincent Cheng 
1620425d2b1cSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
1621425d2b1cSVincent Cheng 
1622425d2b1cSVincent Cheng 	err = _idtcm_adjphase(channel, delta);
1623425d2b1cSVincent Cheng 
16247ea5fda2SMin Li 	if (err)
16257ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16267ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
16277ea5fda2SMin Li 			__LINE__,
16287ea5fda2SMin Li 			__func__);
16297ea5fda2SMin Li 
16307ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
16317ea5fda2SMin Li 
16327ea5fda2SMin Li 	return err;
16337ea5fda2SMin Li }
16347ea5fda2SMin Li 
16357ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
16367ea5fda2SMin Li {
16377ea5fda2SMin Li 	struct idtcm_channel *channel =
16387ea5fda2SMin Li 		container_of(ptp, struct idtcm_channel, caps);
16397ea5fda2SMin Li 
16407ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
16417ea5fda2SMin Li 
16427ea5fda2SMin Li 	int err;
16437ea5fda2SMin Li 
16447ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
16457ea5fda2SMin Li 
16467ea5fda2SMin Li 	err = _idtcm_adjfine(channel, scaled_ppm);
16477ea5fda2SMin Li 
16487ea5fda2SMin Li 	if (err)
16497ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16507ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
16517ea5fda2SMin Li 			__LINE__,
16527ea5fda2SMin Li 			__func__);
16537ea5fda2SMin Li 
1654425d2b1cSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
1655425d2b1cSVincent Cheng 
1656425d2b1cSVincent Cheng 	return err;
1657425d2b1cSVincent Cheng }
1658425d2b1cSVincent Cheng 
16593a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp,
16603a6ba7dcSVincent Cheng 			struct ptp_clock_request *rq, int on)
16613a6ba7dcSVincent Cheng {
16627ea5fda2SMin Li 	int err;
16637ea5fda2SMin Li 
16643a6ba7dcSVincent Cheng 	struct idtcm_channel *channel =
16653a6ba7dcSVincent Cheng 		container_of(ptp, struct idtcm_channel, caps);
16663a6ba7dcSVincent Cheng 
16673a6ba7dcSVincent Cheng 	switch (rq->type) {
16683a6ba7dcSVincent Cheng 	case PTP_CLK_REQ_PEROUT:
16697ea5fda2SMin Li 		if (!on) {
16707ea5fda2SMin Li 			err = idtcm_perout_enable(channel, false, &rq->perout);
16717ea5fda2SMin Li 			if (err)
16727ea5fda2SMin Li 				dev_err(&channel->idtcm->client->dev,
16737ea5fda2SMin Li 					"Failed at line %d in func %s!\n",
16747ea5fda2SMin Li 					__LINE__,
16757ea5fda2SMin Li 					__func__);
16767ea5fda2SMin Li 			return err;
16777ea5fda2SMin Li 		}
16783a6ba7dcSVincent Cheng 
16793a6ba7dcSVincent Cheng 		/* Only accept a 1-PPS aligned to the second. */
16803a6ba7dcSVincent Cheng 		if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
16813a6ba7dcSVincent Cheng 		    rq->perout.period.nsec)
16823a6ba7dcSVincent Cheng 			return -ERANGE;
16833a6ba7dcSVincent Cheng 
16847ea5fda2SMin Li 		err = idtcm_perout_enable(channel, true, &rq->perout);
16857ea5fda2SMin Li 		if (err)
16867ea5fda2SMin Li 			dev_err(&channel->idtcm->client->dev,
16877ea5fda2SMin Li 				"Failed at line %d in func %s!\n",
16887ea5fda2SMin Li 				__LINE__,
16897ea5fda2SMin Li 				__func__);
16907ea5fda2SMin Li 		return err;
16913a6ba7dcSVincent Cheng 	default:
16923a6ba7dcSVincent Cheng 		break;
16933a6ba7dcSVincent Cheng 	}
16943a6ba7dcSVincent Cheng 
16953a6ba7dcSVincent Cheng 	return -EOPNOTSUPP;
16963a6ba7dcSVincent Cheng }
16973a6ba7dcSVincent Cheng 
16987ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm,
16997ea5fda2SMin Li 				u8 pll,
17007ea5fda2SMin Li 				u8 sync_src,
17017ea5fda2SMin Li 				u8 qn,
17027ea5fda2SMin Li 				u8 qn_plus_1)
17037ea5fda2SMin Li {
17047ea5fda2SMin Li 	int err;
17057ea5fda2SMin Li 	u8 val;
17067ea5fda2SMin Li 	u16 dpll;
17077ea5fda2SMin Li 	u16 out0 = 0, out1 = 0;
17087ea5fda2SMin Li 
17097ea5fda2SMin Li 	if ((qn == 0) && (qn_plus_1 == 0))
17107ea5fda2SMin Li 		return 0;
17117ea5fda2SMin Li 
17127ea5fda2SMin Li 	switch (pll) {
17137ea5fda2SMin Li 	case 0:
17147ea5fda2SMin Li 		dpll = DPLL_0;
17157ea5fda2SMin Li 		if (qn)
17167ea5fda2SMin Li 			out0 = OUTPUT_0;
17177ea5fda2SMin Li 		if (qn_plus_1)
17187ea5fda2SMin Li 			out1 = OUTPUT_1;
17197ea5fda2SMin Li 		break;
17207ea5fda2SMin Li 	case 1:
17217ea5fda2SMin Li 		dpll = DPLL_1;
17227ea5fda2SMin Li 		if (qn)
17237ea5fda2SMin Li 			out0 = OUTPUT_2;
17247ea5fda2SMin Li 		if (qn_plus_1)
17257ea5fda2SMin Li 			out1 = OUTPUT_3;
17267ea5fda2SMin Li 		break;
17277ea5fda2SMin Li 	case 2:
17287ea5fda2SMin Li 		dpll = DPLL_2;
17297ea5fda2SMin Li 		if (qn)
17307ea5fda2SMin Li 			out0 = OUTPUT_4;
17317ea5fda2SMin Li 		if (qn_plus_1)
17327ea5fda2SMin Li 			out1 = OUTPUT_5;
17337ea5fda2SMin Li 		break;
17347ea5fda2SMin Li 	case 3:
17357ea5fda2SMin Li 		dpll = DPLL_3;
17367ea5fda2SMin Li 		if (qn)
17377ea5fda2SMin Li 			out0 = OUTPUT_6;
17387ea5fda2SMin Li 		if (qn_plus_1)
17397ea5fda2SMin Li 			out1 = OUTPUT_7;
17407ea5fda2SMin Li 		break;
17417ea5fda2SMin Li 	case 4:
17427ea5fda2SMin Li 		dpll = DPLL_4;
17437ea5fda2SMin Li 		if (qn)
17447ea5fda2SMin Li 			out0 = OUTPUT_8;
17457ea5fda2SMin Li 		break;
17467ea5fda2SMin Li 	case 5:
17477ea5fda2SMin Li 		dpll = DPLL_5;
17487ea5fda2SMin Li 		if (qn)
17497ea5fda2SMin Li 			out0 = OUTPUT_9;
17507ea5fda2SMin Li 		if (qn_plus_1)
17517ea5fda2SMin Li 			out1 = OUTPUT_8;
17527ea5fda2SMin Li 		break;
17537ea5fda2SMin Li 	case 6:
17547ea5fda2SMin Li 		dpll = DPLL_6;
17557ea5fda2SMin Li 		if (qn)
17567ea5fda2SMin Li 			out0 = OUTPUT_10;
17577ea5fda2SMin Li 		if (qn_plus_1)
17587ea5fda2SMin Li 			out1 = OUTPUT_11;
17597ea5fda2SMin Li 		break;
17607ea5fda2SMin Li 	case 7:
17617ea5fda2SMin Li 		dpll = DPLL_7;
17627ea5fda2SMin Li 		if (qn)
17637ea5fda2SMin Li 			out0 = OUTPUT_11;
17647ea5fda2SMin Li 		break;
17657ea5fda2SMin Li 	default:
17667ea5fda2SMin Li 		return -EINVAL;
17677ea5fda2SMin Li 	}
17687ea5fda2SMin Li 
17697ea5fda2SMin Li 	/*
17707ea5fda2SMin Li 	 * Enable OUTPUT OUT_SYNC.
17717ea5fda2SMin Li 	 */
17727ea5fda2SMin Li 	if (out0) {
17737ea5fda2SMin Li 		err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17747ea5fda2SMin Li 
17757ea5fda2SMin Li 		if (err)
17767ea5fda2SMin Li 			return err;
17777ea5fda2SMin Li 
17787ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17797ea5fda2SMin Li 
17807ea5fda2SMin Li 		err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17817ea5fda2SMin Li 
17827ea5fda2SMin Li 		if (err)
17837ea5fda2SMin Li 			return err;
17847ea5fda2SMin Li 	}
17857ea5fda2SMin Li 
17867ea5fda2SMin Li 	if (out1) {
17877ea5fda2SMin Li 		err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17887ea5fda2SMin Li 
17897ea5fda2SMin Li 		if (err)
17907ea5fda2SMin Li 			return err;
17917ea5fda2SMin Li 
17927ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17937ea5fda2SMin Li 
17947ea5fda2SMin Li 		err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17957ea5fda2SMin Li 
17967ea5fda2SMin Li 		if (err)
17977ea5fda2SMin Li 			return err;
17987ea5fda2SMin Li 	}
17997ea5fda2SMin Li 
18007ea5fda2SMin Li 	/* enable dpll sync tod pps, must be set before dpll_mode */
18017ea5fda2SMin Li 	err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
18027ea5fda2SMin Li 	if (err)
18037ea5fda2SMin Li 		return err;
18047ea5fda2SMin Li 
18057ea5fda2SMin Li 	val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
18067ea5fda2SMin Li 	val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
18077ea5fda2SMin Li 	val |= TOD_SYNC_EN;
18087ea5fda2SMin Li 
18097ea5fda2SMin Li 	return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
18107ea5fda2SMin Li }
18117ea5fda2SMin Li 
18127ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel)
18137ea5fda2SMin Li {
18147ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
18157ea5fda2SMin Li 
18167ea5fda2SMin Li 	u8 pll;
18177ea5fda2SMin Li 	u8 sync_src;
18187ea5fda2SMin Li 	u8 qn;
18197ea5fda2SMin Li 	u8 qn_plus_1;
18207ea5fda2SMin Li 	u8 cfg;
18217ea5fda2SMin Li 	int err = 0;
18227ea5fda2SMin Li 	u16 output_mask = channel->output_mask;
18237ea5fda2SMin Li 	u8 out8_mux = 0;
18247ea5fda2SMin Li 	u8 out11_mux = 0;
18257ea5fda2SMin Li 	u8 temp;
18267ea5fda2SMin Li 
18277ea5fda2SMin Li 	/*
18287ea5fda2SMin Li 	 * set tod_out_sync_enable to 0.
18297ea5fda2SMin Li 	 */
18307ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18317ea5fda2SMin Li 	if (err)
18327ea5fda2SMin Li 		return err;
18337ea5fda2SMin Li 
18347ea5fda2SMin Li 	cfg &= ~TOD_OUT_SYNC_ENABLE;
18357ea5fda2SMin Li 
18367ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18377ea5fda2SMin Li 	if (err)
18387ea5fda2SMin Li 		return err;
18397ea5fda2SMin Li 
18407ea5fda2SMin Li 	switch (channel->tod_n) {
18417ea5fda2SMin Li 	case TOD_0:
18427ea5fda2SMin Li 		sync_src = 0;
18437ea5fda2SMin Li 		break;
18447ea5fda2SMin Li 	case TOD_1:
18457ea5fda2SMin Li 		sync_src = 1;
18467ea5fda2SMin Li 		break;
18477ea5fda2SMin Li 	case TOD_2:
18487ea5fda2SMin Li 		sync_src = 2;
18497ea5fda2SMin Li 		break;
18507ea5fda2SMin Li 	case TOD_3:
18517ea5fda2SMin Li 		sync_src = 3;
18527ea5fda2SMin Li 		break;
18537ea5fda2SMin Li 	default:
18547ea5fda2SMin Li 		return -EINVAL;
18557ea5fda2SMin Li 	}
18567ea5fda2SMin Li 
18577ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
18587ea5fda2SMin Li 			 &temp, sizeof(temp));
18597ea5fda2SMin Li 	if (err)
18607ea5fda2SMin Li 		return err;
18617ea5fda2SMin Li 
18627ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18637ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18647ea5fda2SMin Li 		out8_mux = 1;
18657ea5fda2SMin Li 
18667ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
18677ea5fda2SMin Li 			 &temp, sizeof(temp));
18687ea5fda2SMin Li 	if (err)
18697ea5fda2SMin Li 		return err;
18707ea5fda2SMin Li 
18717ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18727ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18737ea5fda2SMin Li 		out11_mux = 1;
18747ea5fda2SMin Li 
18757ea5fda2SMin Li 	for (pll = 0; pll < 8; pll++) {
18767ea5fda2SMin Li 		qn = 0;
18777ea5fda2SMin Li 		qn_plus_1 = 0;
18787ea5fda2SMin Li 
18797ea5fda2SMin Li 		if (pll < 4) {
18807ea5fda2SMin Li 			/* First 4 pll has 2 outputs */
18817ea5fda2SMin Li 			qn = output_mask & 0x1;
18827ea5fda2SMin Li 			output_mask = output_mask >> 1;
18837ea5fda2SMin Li 			qn_plus_1 = output_mask & 0x1;
18847ea5fda2SMin Li 			output_mask = output_mask >> 1;
18857ea5fda2SMin Li 		} else if (pll == 4) {
18867ea5fda2SMin Li 			if (out8_mux == 0) {
18877ea5fda2SMin Li 				qn = output_mask & 0x1;
18887ea5fda2SMin Li 				output_mask = output_mask >> 1;
18897ea5fda2SMin Li 			}
18907ea5fda2SMin Li 		} else if (pll == 5) {
18917ea5fda2SMin Li 			if (out8_mux) {
18927ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
18937ea5fda2SMin Li 				output_mask = output_mask >> 1;
18947ea5fda2SMin Li 			}
18957ea5fda2SMin Li 			qn = output_mask & 0x1;
18967ea5fda2SMin Li 			output_mask = output_mask >> 1;
18977ea5fda2SMin Li 		} else if (pll == 6) {
18987ea5fda2SMin Li 			qn = output_mask & 0x1;
18997ea5fda2SMin Li 			output_mask = output_mask >> 1;
19007ea5fda2SMin Li 			if (out11_mux) {
19017ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
19027ea5fda2SMin Li 				output_mask = output_mask >> 1;
19037ea5fda2SMin Li 			}
19047ea5fda2SMin Li 		} else if (pll == 7) {
19057ea5fda2SMin Li 			if (out11_mux == 0) {
19067ea5fda2SMin Li 				qn = output_mask & 0x1;
19077ea5fda2SMin Li 				output_mask = output_mask >> 1;
19087ea5fda2SMin Li 			}
19097ea5fda2SMin Li 		}
19107ea5fda2SMin Li 
19117ea5fda2SMin Li 		if ((qn != 0) || (qn_plus_1 != 0))
19127ea5fda2SMin Li 			err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
19137ea5fda2SMin Li 					       qn_plus_1);
19147ea5fda2SMin Li 
19157ea5fda2SMin Li 		if (err)
19167ea5fda2SMin Li 			return err;
19177ea5fda2SMin Li 	}
19187ea5fda2SMin Li 
19197ea5fda2SMin Li 	return err;
19207ea5fda2SMin Li }
19217ea5fda2SMin Li 
19223a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel)
19233a6ba7dcSVincent Cheng {
19243a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
19253a6ba7dcSVincent Cheng 	struct timespec64 ts = {0, 0};
19263a6ba7dcSVincent Cheng 	u8 cfg;
19273a6ba7dcSVincent Cheng 	int err;
19283a6ba7dcSVincent Cheng 
19293a6ba7dcSVincent Cheng 	/*
19303a6ba7dcSVincent Cheng 	 * Start the TOD clock ticking.
19313a6ba7dcSVincent Cheng 	 */
19323a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19333a6ba7dcSVincent Cheng 	if (err)
19343a6ba7dcSVincent Cheng 		return err;
19353a6ba7dcSVincent Cheng 
19363a6ba7dcSVincent Cheng 	cfg |= TOD_ENABLE;
19373a6ba7dcSVincent Cheng 
19383a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19393a6ba7dcSVincent Cheng 	if (err)
19403a6ba7dcSVincent Cheng 		return err;
19413a6ba7dcSVincent Cheng 
1942*da948233SMin Li 	if (idtcm->deprecated)
1943*da948233SMin Li 		return _idtcm_settime_deprecated(channel, &ts);
1944*da948233SMin Li 	else
1945*da948233SMin Li 		return _idtcm_settime(channel, &ts,
1946*da948233SMin Li 				      SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
19473a6ba7dcSVincent Cheng }
19483a6ba7dcSVincent Cheng 
1949*da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm)
19503a6ba7dcSVincent Cheng {
19513a6ba7dcSVincent Cheng 	u8 major;
19523a6ba7dcSVincent Cheng 	u8 minor;
19533a6ba7dcSVincent Cheng 	u8 hotfix;
19543a6ba7dcSVincent Cheng 	u16 product_id;
19553a6ba7dcSVincent Cheng 	u8 hw_rev_id;
19561ece2fbeSVincent Cheng 	u8 config_select;
19571ece2fbeSVincent Cheng 	char *fmt = "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d\n";
19583a6ba7dcSVincent Cheng 
19593a6ba7dcSVincent Cheng 	idtcm_read_major_release(idtcm, &major);
19603a6ba7dcSVincent Cheng 	idtcm_read_minor_release(idtcm, &minor);
19613a6ba7dcSVincent Cheng 	idtcm_read_hotfix_release(idtcm, &hotfix);
19623a6ba7dcSVincent Cheng 
19633a6ba7dcSVincent Cheng 	idtcm_read_product_id(idtcm, &product_id);
19643a6ba7dcSVincent Cheng 	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
19653a6ba7dcSVincent Cheng 
19661ece2fbeSVincent Cheng 	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
19671ece2fbeSVincent Cheng 
19687ea5fda2SMin Li 	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
19697ea5fda2SMin Li 		 major, minor, hotfix);
19707ea5fda2SMin Li 
1971*da948233SMin Li 	if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
1972*da948233SMin Li 		idtcm->deprecated = 0;
1973*da948233SMin Li 	else
1974*da948233SMin Li 		idtcm->deprecated = 1;
1975*da948233SMin Li 
19761ece2fbeSVincent Cheng 	dev_info(&idtcm->client->dev, fmt, major, minor, hotfix,
19771ece2fbeSVincent Cheng 		 product_id, hw_rev_id, config_select);
19783a6ba7dcSVincent Cheng }
19793a6ba7dcSVincent Cheng 
19806485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = {
19813a6ba7dcSVincent Cheng 	.owner		= THIS_MODULE,
19823a6ba7dcSVincent Cheng 	.max_adj	= 244000,
19837ea5fda2SMin Li 	.n_per_out	= 12,
1984425d2b1cSVincent Cheng 	.adjphase	= &idtcm_adjphase,
19857ea5fda2SMin Li 	.adjfine	= &idtcm_adjfine,
19863a6ba7dcSVincent Cheng 	.adjtime	= &idtcm_adjtime,
19873a6ba7dcSVincent Cheng 	.gettime64	= &idtcm_gettime,
19883a6ba7dcSVincent Cheng 	.settime64	= &idtcm_settime,
19893a6ba7dcSVincent Cheng 	.enable		= &idtcm_enable,
19903a6ba7dcSVincent Cheng };
19913a6ba7dcSVincent Cheng 
1992*da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = {
1993*da948233SMin Li 	.owner		= THIS_MODULE,
1994*da948233SMin Li 	.max_adj	= 244000,
1995*da948233SMin Li 	.n_per_out	= 12,
1996*da948233SMin Li 	.adjphase	= &idtcm_adjphase,
1997*da948233SMin Li 	.adjfine	= &idtcm_adjfine,
1998*da948233SMin Li 	.adjtime	= &idtcm_adjtime_deprecated,
1999*da948233SMin Li 	.gettime64	= &idtcm_gettime,
2000*da948233SMin Li 	.settime64	= &idtcm_settime_deprecated,
2001*da948233SMin Li 	.enable		= &idtcm_enable,
2002*da948233SMin Li };
2003*da948233SMin Li 
20047ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel)
20053a6ba7dcSVincent Cheng {
20067ea5fda2SMin Li 	int err = 0;
20073a6ba7dcSVincent Cheng 
20087ea5fda2SMin Li 	switch (channel->pll) {
20093a6ba7dcSVincent Cheng 	case 0:
20103a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_0;
20113a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_0;
20123a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_0;
20133a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_0;
20143a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_0;
20153a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
20163a6ba7dcSVincent Cheng 		break;
20173a6ba7dcSVincent Cheng 	case 1:
20183a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_1;
20193a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_1;
20203a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_1;
20213a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_1;
20223a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_1;
20233a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
20243a6ba7dcSVincent Cheng 		break;
20253a6ba7dcSVincent Cheng 	case 2:
20263a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_2;
20273a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_2;
20283a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_2;
20293a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_2;
20303a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_2;
20313a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
20323a6ba7dcSVincent Cheng 		break;
20333a6ba7dcSVincent Cheng 	case 3:
20343a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_3;
20353a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_3;
20363a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_3;
20373a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_3;
20383a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_3;
20393a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
20403a6ba7dcSVincent Cheng 		break;
20417ea5fda2SMin Li 	case 4:
20427ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_4;
20437ea5fda2SMin Li 		channel->dpll_n = DPLL_4;
20447ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_4;
20457ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_4;
20467ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_4;
20477ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
20487ea5fda2SMin Li 		break;
20497ea5fda2SMin Li 	case 5:
20507ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_5;
20517ea5fda2SMin Li 		channel->dpll_n = DPLL_5;
20527ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_5;
20537ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_5;
20547ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_5;
20557ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
20567ea5fda2SMin Li 		break;
20577ea5fda2SMin Li 	case 6:
20587ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_6;
20597ea5fda2SMin Li 		channel->dpll_n = DPLL_6;
20607ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_6;
20617ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_6;
20627ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_6;
20637ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
20647ea5fda2SMin Li 		break;
20657ea5fda2SMin Li 	case 7:
20667ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_7;
20677ea5fda2SMin Li 		channel->dpll_n = DPLL_7;
20687ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_7;
20697ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_7;
20707ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_7;
20717ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
20727ea5fda2SMin Li 		break;
20737ea5fda2SMin Li 	default:
20747ea5fda2SMin Li 		err = -EINVAL;
20757ea5fda2SMin Li 	}
20767ea5fda2SMin Li 
20777ea5fda2SMin Li 	return err;
20787ea5fda2SMin Li }
20797ea5fda2SMin Li 
20807ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
20817ea5fda2SMin Li {
20827ea5fda2SMin Li 	struct idtcm_channel *channel;
20837ea5fda2SMin Li 	int err;
20847ea5fda2SMin Li 
20857ea5fda2SMin Li 	if (!(index < MAX_TOD))
20867ea5fda2SMin Li 		return -EINVAL;
20877ea5fda2SMin Li 
20887ea5fda2SMin Li 	channel = &idtcm->channel[index];
20897ea5fda2SMin Li 
20907ea5fda2SMin Li 	/* Set pll addresses */
20917ea5fda2SMin Li 	err = configure_channel_pll(channel);
20927ea5fda2SMin Li 	if (err)
20937ea5fda2SMin Li 		return err;
20947ea5fda2SMin Li 
20957ea5fda2SMin Li 	/* Set tod addresses */
20967ea5fda2SMin Li 	switch (index) {
20977ea5fda2SMin Li 	case 0:
20987ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_0;
20997ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_0;
21007ea5fda2SMin Li 		channel->tod_n = TOD_0;
21017ea5fda2SMin Li 		break;
21027ea5fda2SMin Li 	case 1:
21037ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_1;
21047ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_1;
21057ea5fda2SMin Li 		channel->tod_n = TOD_1;
21067ea5fda2SMin Li 		break;
21077ea5fda2SMin Li 	case 2:
21087ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_2;
21097ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_2;
21107ea5fda2SMin Li 		channel->tod_n = TOD_2;
21117ea5fda2SMin Li 		break;
21127ea5fda2SMin Li 	case 3:
21137ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_3;
21147ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_3;
21157ea5fda2SMin Li 		channel->tod_n = TOD_3;
21167ea5fda2SMin Li 		break;
21173a6ba7dcSVincent Cheng 	default:
21183a6ba7dcSVincent Cheng 		return -EINVAL;
21193a6ba7dcSVincent Cheng 	}
21203a6ba7dcSVincent Cheng 
21213a6ba7dcSVincent Cheng 	channel->idtcm = idtcm;
21223a6ba7dcSVincent Cheng 
2123*da948233SMin Li 	if (idtcm->deprecated)
2124*da948233SMin Li 		channel->caps = idtcm_caps_deprecated;
21257ea5fda2SMin Li 	else
21263a6ba7dcSVincent Cheng 		channel->caps = idtcm_caps;
21277ea5fda2SMin Li 
21283a6ba7dcSVincent Cheng 	snprintf(channel->caps.name, sizeof(channel->caps.name),
21297ea5fda2SMin Li 		 "IDT CM TOD%u", index);
21307ea5fda2SMin Li 
2131*da948233SMin Li 	if (!idtcm->deprecated) {
21327ea5fda2SMin Li 		err = idtcm_enable_tod_sync(channel);
21337ea5fda2SMin Li 		if (err) {
21347ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
21357ea5fda2SMin Li 				"Failed at line %d in func %s!\n",
21367ea5fda2SMin Li 				__LINE__,
21377ea5fda2SMin Li 				__func__);
21387ea5fda2SMin Li 			return err;
21397ea5fda2SMin Li 		}
21407ea5fda2SMin Li 	}
21413a6ba7dcSVincent Cheng 
21427260d1c8SMin Li 	/* Sync pll mode with hardware */
21437260d1c8SMin Li 	err = idtcm_get_pll_mode(channel, &channel->pll_mode);
21447ea5fda2SMin Li 	if (err) {
21457ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21467260d1c8SMin Li 			"Error: %s - Unable to read pll mode\n", __func__);
21473a6ba7dcSVincent Cheng 		return err;
21487ea5fda2SMin Li 	}
21493a6ba7dcSVincent Cheng 
21503a6ba7dcSVincent Cheng 	err = idtcm_enable_tod(channel);
21517ea5fda2SMin Li 	if (err) {
21527ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21537ea5fda2SMin Li 			"Failed at line %d in func %s!\n",
21547ea5fda2SMin Li 			__LINE__,
21557ea5fda2SMin Li 			__func__);
21563a6ba7dcSVincent Cheng 		return err;
21577ea5fda2SMin Li 	}
21583a6ba7dcSVincent Cheng 
21593a6ba7dcSVincent Cheng 	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
21603a6ba7dcSVincent Cheng 
21613a6ba7dcSVincent Cheng 	if (IS_ERR(channel->ptp_clock)) {
21623a6ba7dcSVincent Cheng 		err = PTR_ERR(channel->ptp_clock);
21633a6ba7dcSVincent Cheng 		channel->ptp_clock = NULL;
21643a6ba7dcSVincent Cheng 		return err;
21653a6ba7dcSVincent Cheng 	}
21663a6ba7dcSVincent Cheng 
21673a6ba7dcSVincent Cheng 	if (!channel->ptp_clock)
21683a6ba7dcSVincent Cheng 		return -ENOTSUPP;
21693a6ba7dcSVincent Cheng 
21703a6ba7dcSVincent Cheng 	dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
21713a6ba7dcSVincent Cheng 		 index, channel->ptp_clock->index);
21723a6ba7dcSVincent Cheng 
21733a6ba7dcSVincent Cheng 	return 0;
21743a6ba7dcSVincent Cheng }
21753a6ba7dcSVincent Cheng 
21763a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm)
21773a6ba7dcSVincent Cheng {
21783a6ba7dcSVincent Cheng 	u8 i;
21793a6ba7dcSVincent Cheng 	struct idtcm_channel *channel;
21803a6ba7dcSVincent Cheng 
21817ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
21823a6ba7dcSVincent Cheng 
21833a6ba7dcSVincent Cheng 		channel = &idtcm->channel[i];
21843a6ba7dcSVincent Cheng 
21853a6ba7dcSVincent Cheng 		if (channel->ptp_clock)
21863a6ba7dcSVincent Cheng 			ptp_clock_unregister(channel->ptp_clock);
21873a6ba7dcSVincent Cheng 	}
21883a6ba7dcSVincent Cheng }
21893a6ba7dcSVincent Cheng 
21903a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm)
21913a6ba7dcSVincent Cheng {
21927ea5fda2SMin Li 	idtcm->tod_mask = DEFAULT_TOD_MASK;
21937ea5fda2SMin Li 
21947ea5fda2SMin Li 	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
21957ea5fda2SMin Li 	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
21967ea5fda2SMin Li 	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
21977ea5fda2SMin Li 	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
21983a6ba7dcSVincent Cheng 
21993a6ba7dcSVincent Cheng 	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
22003a6ba7dcSVincent Cheng 	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
22013a6ba7dcSVincent Cheng 	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
22023a6ba7dcSVincent Cheng 	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
22033a6ba7dcSVincent Cheng }
22043a6ba7dcSVincent Cheng 
22053a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client,
22063a6ba7dcSVincent Cheng 		       const struct i2c_device_id *id)
22073a6ba7dcSVincent Cheng {
22083a6ba7dcSVincent Cheng 	struct idtcm *idtcm;
22093a6ba7dcSVincent Cheng 	int err;
22103a6ba7dcSVincent Cheng 	u8 i;
22117ea5fda2SMin Li 	char *fmt = "Failed at %d in line %s with channel output %d!\n";
22123a6ba7dcSVincent Cheng 
22133a6ba7dcSVincent Cheng 	/* Unused for now */
22143a6ba7dcSVincent Cheng 	(void)id;
22153a6ba7dcSVincent Cheng 
22163a6ba7dcSVincent Cheng 	idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
22173a6ba7dcSVincent Cheng 
22183a6ba7dcSVincent Cheng 	if (!idtcm)
22193a6ba7dcSVincent Cheng 		return -ENOMEM;
22203a6ba7dcSVincent Cheng 
22213a6ba7dcSVincent Cheng 	idtcm->client = client;
22223a6ba7dcSVincent Cheng 	idtcm->page_offset = 0xff;
22233a6ba7dcSVincent Cheng 	idtcm->calculate_overhead_flag = 0;
22243a6ba7dcSVincent Cheng 
22253a6ba7dcSVincent Cheng 	set_default_masks(idtcm);
22263a6ba7dcSVincent Cheng 
22273a6ba7dcSVincent Cheng 	mutex_init(&idtcm->reg_lock);
22283a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
22293a6ba7dcSVincent Cheng 
2230*da948233SMin Li 	idtcm_set_version_info(idtcm);
22313a6ba7dcSVincent Cheng 
22323a6ba7dcSVincent Cheng 	err = idtcm_load_firmware(idtcm, &client->dev);
22333a6ba7dcSVincent Cheng 
22343a6ba7dcSVincent Cheng 	if (err)
22353a6ba7dcSVincent Cheng 		dev_warn(&idtcm->client->dev,
22363a6ba7dcSVincent Cheng 			 "loading firmware failed with %d\n", err);
22373a6ba7dcSVincent Cheng 
2238251f4fe2SMin Li 	if (wait_for_boot_status_ready(idtcm))
2239251f4fe2SMin Li 		dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0\n");
2240251f4fe2SMin Li 
22417ea5fda2SMin Li 	if (idtcm->tod_mask) {
22427ea5fda2SMin Li 		for (i = 0; i < MAX_TOD; i++) {
22437ea5fda2SMin Li 			if (idtcm->tod_mask & (1 << i)) {
22443a6ba7dcSVincent Cheng 				err = idtcm_enable_channel(idtcm, i);
22457ea5fda2SMin Li 				if (err) {
22467ea5fda2SMin Li 					dev_err(&idtcm->client->dev,
22477ea5fda2SMin Li 						fmt,
22487ea5fda2SMin Li 						__LINE__,
22497ea5fda2SMin Li 						__func__,
22507ea5fda2SMin Li 						i);
22513a6ba7dcSVincent Cheng 					break;
22523a6ba7dcSVincent Cheng 				}
22533a6ba7dcSVincent Cheng 			}
22547ea5fda2SMin Li 		}
22553a6ba7dcSVincent Cheng 	} else {
22563a6ba7dcSVincent Cheng 		dev_err(&idtcm->client->dev,
22573a6ba7dcSVincent Cheng 			"no PLLs flagged as PHCs, nothing to do\n");
22583a6ba7dcSVincent Cheng 		err = -ENODEV;
22593a6ba7dcSVincent Cheng 	}
22603a6ba7dcSVincent Cheng 
22613a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
22623a6ba7dcSVincent Cheng 
22633a6ba7dcSVincent Cheng 	if (err) {
22643a6ba7dcSVincent Cheng 		ptp_clock_unregister_all(idtcm);
22653a6ba7dcSVincent Cheng 		return err;
22663a6ba7dcSVincent Cheng 	}
22673a6ba7dcSVincent Cheng 
22683a6ba7dcSVincent Cheng 	i2c_set_clientdata(client, idtcm);
22693a6ba7dcSVincent Cheng 
22703a6ba7dcSVincent Cheng 	return 0;
22713a6ba7dcSVincent Cheng }
22723a6ba7dcSVincent Cheng 
22733a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client)
22743a6ba7dcSVincent Cheng {
22753a6ba7dcSVincent Cheng 	struct idtcm *idtcm = i2c_get_clientdata(client);
22763a6ba7dcSVincent Cheng 
22773a6ba7dcSVincent Cheng 	ptp_clock_unregister_all(idtcm);
22783a6ba7dcSVincent Cheng 
22793a6ba7dcSVincent Cheng 	mutex_destroy(&idtcm->reg_lock);
22803a6ba7dcSVincent Cheng 
22813a6ba7dcSVincent Cheng 	return 0;
22823a6ba7dcSVincent Cheng }
22833a6ba7dcSVincent Cheng 
22843a6ba7dcSVincent Cheng #ifdef CONFIG_OF
22853a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = {
22863a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34000" },
22873a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34001" },
22883a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34002" },
22893a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34003" },
22903a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34004" },
22913a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34005" },
22923a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34006" },
22933a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34007" },
22943a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34008" },
22953a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34009" },
22963a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34010" },
22973a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34011" },
22983a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34012" },
22993a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34013" },
23003a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34014" },
23013a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34015" },
23023a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34016" },
23033a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34017" },
23043a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34018" },
23053a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34019" },
23063a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34040" },
23073a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34041" },
23083a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34042" },
23093a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34043" },
23103a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34044" },
23113a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34045" },
23123a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34046" },
23133a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34047" },
23143a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34048" },
23153a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34049" },
23163a6ba7dcSVincent Cheng 	{},
23173a6ba7dcSVincent Cheng };
23183a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id);
23193a6ba7dcSVincent Cheng #endif
23203a6ba7dcSVincent Cheng 
23213a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = {
23223a6ba7dcSVincent Cheng 	{ "8a34000" },
23233a6ba7dcSVincent Cheng 	{ "8a34001" },
23243a6ba7dcSVincent Cheng 	{ "8a34002" },
23253a6ba7dcSVincent Cheng 	{ "8a34003" },
23263a6ba7dcSVincent Cheng 	{ "8a34004" },
23273a6ba7dcSVincent Cheng 	{ "8a34005" },
23283a6ba7dcSVincent Cheng 	{ "8a34006" },
23293a6ba7dcSVincent Cheng 	{ "8a34007" },
23303a6ba7dcSVincent Cheng 	{ "8a34008" },
23313a6ba7dcSVincent Cheng 	{ "8a34009" },
23323a6ba7dcSVincent Cheng 	{ "8a34010" },
23333a6ba7dcSVincent Cheng 	{ "8a34011" },
23343a6ba7dcSVincent Cheng 	{ "8a34012" },
23353a6ba7dcSVincent Cheng 	{ "8a34013" },
23363a6ba7dcSVincent Cheng 	{ "8a34014" },
23373a6ba7dcSVincent Cheng 	{ "8a34015" },
23383a6ba7dcSVincent Cheng 	{ "8a34016" },
23393a6ba7dcSVincent Cheng 	{ "8a34017" },
23403a6ba7dcSVincent Cheng 	{ "8a34018" },
23413a6ba7dcSVincent Cheng 	{ "8a34019" },
23423a6ba7dcSVincent Cheng 	{ "8a34040" },
23433a6ba7dcSVincent Cheng 	{ "8a34041" },
23443a6ba7dcSVincent Cheng 	{ "8a34042" },
23453a6ba7dcSVincent Cheng 	{ "8a34043" },
23463a6ba7dcSVincent Cheng 	{ "8a34044" },
23473a6ba7dcSVincent Cheng 	{ "8a34045" },
23483a6ba7dcSVincent Cheng 	{ "8a34046" },
23493a6ba7dcSVincent Cheng 	{ "8a34047" },
23503a6ba7dcSVincent Cheng 	{ "8a34048" },
23513a6ba7dcSVincent Cheng 	{ "8a34049" },
23523a6ba7dcSVincent Cheng 	{},
23533a6ba7dcSVincent Cheng };
23543a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
23553a6ba7dcSVincent Cheng 
23563a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = {
23573a6ba7dcSVincent Cheng 	.driver = {
23583a6ba7dcSVincent Cheng 		.of_match_table	= of_match_ptr(idtcm_dt_id),
23593a6ba7dcSVincent Cheng 		.name		= "idtcm",
23603a6ba7dcSVincent Cheng 	},
23613a6ba7dcSVincent Cheng 	.probe		= idtcm_probe,
23623a6ba7dcSVincent Cheng 	.remove		= idtcm_remove,
23633a6ba7dcSVincent Cheng 	.id_table	= idtcm_i2c_id,
23643a6ba7dcSVincent Cheng };
23653a6ba7dcSVincent Cheng 
23663a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver);
2367