xref: /openbmc/linux/drivers/ptp/ptp_clockmatrix.c (revision fde3b3a7)
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;
1633a6ba7dcSVincent Cheng 
1643a6ba7dcSVincent Cheng 	msg[0].addr = client->addr;
1653a6ba7dcSVincent Cheng 	msg[0].flags = 0;
1663a6ba7dcSVincent Cheng 	msg[0].len = 1;
1673a6ba7dcSVincent Cheng 	msg[0].buf = &regaddr;
1683a6ba7dcSVincent Cheng 
1693a6ba7dcSVincent Cheng 	msg[1].addr = client->addr;
170957ff427SMin Li 	msg[1].flags = I2C_M_RD;
1713a6ba7dcSVincent Cheng 	msg[1].len = count;
1723a6ba7dcSVincent Cheng 	msg[1].buf = buf;
1733a6ba7dcSVincent Cheng 
1743a6ba7dcSVincent Cheng 	cnt = i2c_transfer(client->adapter, msg, 2);
1753a6ba7dcSVincent Cheng 
1763a6ba7dcSVincent Cheng 	if (cnt < 0) {
1777ea5fda2SMin Li 		dev_err(&client->dev,
1781c49d3e9SVincent Cheng 			"i2c_transfer failed at %d in %s, at addr: %04x!",
1791c49d3e9SVincent Cheng 			__LINE__, __func__, regaddr);
1803a6ba7dcSVincent Cheng 		return cnt;
1813a6ba7dcSVincent Cheng 	} else if (cnt != 2) {
1823a6ba7dcSVincent Cheng 		dev_err(&client->dev,
1831c49d3e9SVincent Cheng 			"i2c_transfer sent only %d of %d messages", cnt, 2);
1843a6ba7dcSVincent Cheng 		return -EIO;
1853a6ba7dcSVincent Cheng 	}
1863a6ba7dcSVincent Cheng 
1873a6ba7dcSVincent Cheng 	return 0;
1883a6ba7dcSVincent Cheng }
1893a6ba7dcSVincent Cheng 
190957ff427SMin Li static int idtcm_xfer_write(struct idtcm *idtcm,
191957ff427SMin Li 			    u8 regaddr,
192957ff427SMin Li 			    u8 *buf,
193957ff427SMin Li 			    u16 count)
194957ff427SMin Li {
195957ff427SMin Li 	struct i2c_client *client = idtcm->client;
196957ff427SMin Li 	/* we add 1 byte for device register */
197957ff427SMin Li 	u8 msg[IDTCM_MAX_WRITE_COUNT + 1];
198957ff427SMin Li 	int cnt;
199957ff427SMin Li 
200957ff427SMin Li 	if (count > IDTCM_MAX_WRITE_COUNT)
201957ff427SMin Li 		return -EINVAL;
202957ff427SMin Li 
203957ff427SMin Li 	msg[0] = regaddr;
204957ff427SMin Li 	memcpy(&msg[1], buf, count);
205957ff427SMin Li 
206957ff427SMin Li 	cnt = i2c_master_send(client, msg, count + 1);
207957ff427SMin Li 
208957ff427SMin Li 	if (cnt < 0) {
209957ff427SMin Li 		dev_err(&client->dev,
2101c49d3e9SVincent Cheng 			"i2c_master_send failed at %d in %s, at addr: %04x!",
2111c49d3e9SVincent Cheng 			__LINE__, __func__, regaddr);
212957ff427SMin Li 		return cnt;
213957ff427SMin Li 	}
214957ff427SMin Li 
215957ff427SMin Li 	return 0;
216957ff427SMin Li }
217957ff427SMin Li 
2183a6ba7dcSVincent Cheng static int idtcm_page_offset(struct idtcm *idtcm, u8 val)
2193a6ba7dcSVincent Cheng {
2203a6ba7dcSVincent Cheng 	u8 buf[4];
2213a6ba7dcSVincent Cheng 	int err;
2223a6ba7dcSVincent Cheng 
2233a6ba7dcSVincent Cheng 	if (idtcm->page_offset == val)
2243a6ba7dcSVincent Cheng 		return 0;
2253a6ba7dcSVincent Cheng 
2263a6ba7dcSVincent Cheng 	buf[0] = 0x0;
2273a6ba7dcSVincent Cheng 	buf[1] = val;
2283a6ba7dcSVincent Cheng 	buf[2] = 0x10;
2293a6ba7dcSVincent Cheng 	buf[3] = 0x20;
2303a6ba7dcSVincent Cheng 
231957ff427SMin Li 	err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf));
2327ea5fda2SMin Li 	if (err) {
2337ea5fda2SMin Li 		idtcm->page_offset = 0xff;
2341c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "failed to set page offset");
2357ea5fda2SMin Li 	} else {
2363a6ba7dcSVincent Cheng 		idtcm->page_offset = val;
2377ea5fda2SMin Li 	}
2383a6ba7dcSVincent Cheng 
2393a6ba7dcSVincent Cheng 	return err;
2403a6ba7dcSVincent Cheng }
2413a6ba7dcSVincent Cheng 
2423a6ba7dcSVincent Cheng static int _idtcm_rdwr(struct idtcm *idtcm,
2433a6ba7dcSVincent Cheng 		       u16 regaddr,
2443a6ba7dcSVincent Cheng 		       u8 *buf,
2453a6ba7dcSVincent Cheng 		       u16 count,
2463a6ba7dcSVincent Cheng 		       bool write)
2473a6ba7dcSVincent Cheng {
2483a6ba7dcSVincent Cheng 	u8 hi;
2493a6ba7dcSVincent Cheng 	u8 lo;
2503a6ba7dcSVincent Cheng 	int err;
2513a6ba7dcSVincent Cheng 
2523a6ba7dcSVincent Cheng 	hi = (regaddr >> 8) & 0xff;
2533a6ba7dcSVincent Cheng 	lo = regaddr & 0xff;
2543a6ba7dcSVincent Cheng 
2553a6ba7dcSVincent Cheng 	err = idtcm_page_offset(idtcm, hi);
2563a6ba7dcSVincent Cheng 	if (err)
2573a6ba7dcSVincent Cheng 		return err;
258957ff427SMin Li 
259957ff427SMin Li 	if (write)
260957ff427SMin Li 		return idtcm_xfer_write(idtcm, lo, buf, count);
261957ff427SMin Li 
262957ff427SMin Li 	return idtcm_xfer_read(idtcm, lo, buf, count);
2633a6ba7dcSVincent Cheng }
2643a6ba7dcSVincent Cheng 
2653a6ba7dcSVincent Cheng static int idtcm_read(struct idtcm *idtcm,
2663a6ba7dcSVincent Cheng 		      u16 module,
2673a6ba7dcSVincent Cheng 		      u16 regaddr,
2683a6ba7dcSVincent Cheng 		      u8 *buf,
2693a6ba7dcSVincent Cheng 		      u16 count)
2703a6ba7dcSVincent Cheng {
2713a6ba7dcSVincent Cheng 	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false);
2723a6ba7dcSVincent Cheng }
2733a6ba7dcSVincent Cheng 
2743a6ba7dcSVincent Cheng static int idtcm_write(struct idtcm *idtcm,
2753a6ba7dcSVincent Cheng 		       u16 module,
2763a6ba7dcSVincent Cheng 		       u16 regaddr,
2773a6ba7dcSVincent Cheng 		       u8 *buf,
2783a6ba7dcSVincent Cheng 		       u16 count)
2793a6ba7dcSVincent Cheng {
2803a6ba7dcSVincent Cheng 	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
2813a6ba7dcSVincent Cheng }
2823a6ba7dcSVincent Cheng 
283251f4fe2SMin Li static int clear_boot_status(struct idtcm *idtcm)
284251f4fe2SMin Li {
285251f4fe2SMin Li 	u8 buf[4] = {0};
286251f4fe2SMin Li 
287*fde3b3a7SVincent Cheng 	return idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
288251f4fe2SMin Li }
289251f4fe2SMin Li 
290251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status)
291251f4fe2SMin Li {
292251f4fe2SMin Li 	int err;
293251f4fe2SMin Li 	u8 buf[4] = {0};
294251f4fe2SMin Li 
295251f4fe2SMin Li 	err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
296251f4fe2SMin Li 
297251f4fe2SMin Li 	*status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
298251f4fe2SMin Li 
299251f4fe2SMin Li 	return err;
300251f4fe2SMin Li }
301251f4fe2SMin Li 
302251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm)
303251f4fe2SMin Li {
304251f4fe2SMin Li 	u32 status = 0;
305251f4fe2SMin Li 	u8 i = 30;	/* 30 * 100ms = 3s */
306251f4fe2SMin Li 	int err;
307251f4fe2SMin Li 
308251f4fe2SMin Li 	do {
309251f4fe2SMin Li 		err = read_boot_status(idtcm, &status);
310251f4fe2SMin Li 		if (err)
311251f4fe2SMin Li 			return err;
312251f4fe2SMin Li 
313251f4fe2SMin Li 		if (status == 0xA0)
314251f4fe2SMin Li 			return 0;
315251f4fe2SMin Li 
316251f4fe2SMin Li 		msleep(100);
317251f4fe2SMin Li 		i--;
318251f4fe2SMin Li 
319251f4fe2SMin Li 	} while (i);
320251f4fe2SMin Li 
3211c49d3e9SVincent Cheng 	dev_warn(&idtcm->client->dev, "%s timed out", __func__);
322251f4fe2SMin Li 
323251f4fe2SMin Li 	return -EBUSY;
324251f4fe2SMin Li }
325251f4fe2SMin Li 
326797d3186SVincent Cheng static int read_sys_apll_status(struct idtcm *idtcm, u8 *status)
327797d3186SVincent Cheng {
328797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status,
329797d3186SVincent Cheng 			  sizeof(u8));
330797d3186SVincent Cheng }
331797d3186SVincent Cheng 
332797d3186SVincent Cheng static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status)
333797d3186SVincent Cheng {
334797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8));
335797d3186SVincent Cheng }
336797d3186SVincent Cheng 
337797d3186SVincent Cheng static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm)
338797d3186SVincent Cheng {
339797d3186SVincent Cheng 	unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS);
340797d3186SVincent Cheng 	u8 apll = 0;
341797d3186SVincent Cheng 	u8 dpll = 0;
342797d3186SVincent Cheng 	int err;
343797d3186SVincent Cheng 
344797d3186SVincent Cheng 	do {
345797d3186SVincent Cheng 		err = read_sys_apll_status(idtcm, &apll);
346797d3186SVincent Cheng 		if (err)
347797d3186SVincent Cheng 			return err;
348797d3186SVincent Cheng 
349797d3186SVincent Cheng 		err = read_sys_dpll_status(idtcm, &dpll);
350797d3186SVincent Cheng 		if (err)
351797d3186SVincent Cheng 			return err;
352797d3186SVincent Cheng 
353797d3186SVincent Cheng 		apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK;
354797d3186SVincent Cheng 		dpll &= DPLL_SYS_STATE_MASK;
355797d3186SVincent Cheng 
356797d3186SVincent Cheng 		if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED &&
357797d3186SVincent Cheng 		    dpll == DPLL_STATE_LOCKED) {
358797d3186SVincent Cheng 			return 0;
359797d3186SVincent Cheng 		} else if (dpll == DPLL_STATE_FREERUN ||
360797d3186SVincent Cheng 			   dpll == DPLL_STATE_HOLDOVER ||
361797d3186SVincent Cheng 			   dpll == DPLL_STATE_OPEN_LOOP) {
362797d3186SVincent Cheng 			dev_warn(&idtcm->client->dev,
363797d3186SVincent Cheng 				"No wait state: DPLL_SYS_STATE %d", dpll);
364797d3186SVincent Cheng 			return -EPERM;
365797d3186SVincent Cheng 		}
366797d3186SVincent Cheng 
367797d3186SVincent Cheng 		msleep(LOCK_POLL_INTERVAL_MS);
368797d3186SVincent Cheng 	} while (time_is_after_jiffies(timeout));
369797d3186SVincent Cheng 
370797d3186SVincent Cheng 	dev_warn(&idtcm->client->dev,
371797d3186SVincent Cheng 		 "%d ms lock timeout: SYS APLL Loss Lock %d  SYS DPLL state %d",
372797d3186SVincent Cheng 		 LOCK_TIMEOUT_MS, apll, dpll);
373797d3186SVincent Cheng 
374797d3186SVincent Cheng 	return -ETIME;
375797d3186SVincent Cheng }
376797d3186SVincent Cheng 
377797d3186SVincent Cheng static void wait_for_chip_ready(struct idtcm *idtcm)
378797d3186SVincent Cheng {
379797d3186SVincent Cheng 	if (wait_for_boot_status_ready(idtcm))
380797d3186SVincent Cheng 		dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0");
381797d3186SVincent Cheng 
382797d3186SVincent Cheng 	if (wait_for_sys_apll_dpll_lock(idtcm))
383797d3186SVincent Cheng 		dev_warn(&idtcm->client->dev,
384797d3186SVincent Cheng 			 "Continuing while SYS APLL/DPLL is not locked");
385797d3186SVincent Cheng }
386797d3186SVincent Cheng 
3873a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel,
3883a6ba7dcSVincent Cheng 			  struct timespec64 *ts)
3893a6ba7dcSVincent Cheng {
3903a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
3913a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
3927ea5fda2SMin Li 	u8 timeout = 10;
3933a6ba7dcSVincent Cheng 	u8 trigger;
3943a6ba7dcSVincent Cheng 	int err;
3953a6ba7dcSVincent Cheng 
3963a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
3973a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
3983a6ba7dcSVincent Cheng 	if (err)
3993a6ba7dcSVincent Cheng 		return err;
4003a6ba7dcSVincent Cheng 
4013a6ba7dcSVincent Cheng 	trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
4023a6ba7dcSVincent Cheng 	trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
4037ea5fda2SMin Li 	trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
4043a6ba7dcSVincent Cheng 
4053a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_read_primary,
4063a6ba7dcSVincent Cheng 			  TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
4077ea5fda2SMin Li 	if (err)
4087ea5fda2SMin Li 		return err;
4097ea5fda2SMin Li 
4107ea5fda2SMin Li 	/* wait trigger to be 0 */
4117ea5fda2SMin Li 	while (trigger & TOD_READ_TRIGGER_MASK) {
4127ea5fda2SMin Li 		if (idtcm->calculate_overhead_flag)
4137ea5fda2SMin Li 			idtcm->start_time = ktime_get_raw();
4147ea5fda2SMin Li 
4157ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_read_primary,
4167ea5fda2SMin Li 				 TOD_READ_PRIMARY_CMD, &trigger,
4177ea5fda2SMin Li 				 sizeof(trigger));
4183a6ba7dcSVincent Cheng 		if (err)
4193a6ba7dcSVincent Cheng 			return err;
4203a6ba7dcSVincent Cheng 
4217ea5fda2SMin Li 		if (--timeout == 0)
4227ea5fda2SMin Li 			return -EIO;
4237ea5fda2SMin Li 	}
4243a6ba7dcSVincent Cheng 
4253a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
4263a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY, buf, sizeof(buf));
4273a6ba7dcSVincent Cheng 	if (err)
4283a6ba7dcSVincent Cheng 		return err;
4293a6ba7dcSVincent Cheng 
4303a6ba7dcSVincent Cheng 	err = char_array_to_timespec(buf, sizeof(buf), ts);
4313a6ba7dcSVincent Cheng 
4323a6ba7dcSVincent Cheng 	return err;
4333a6ba7dcSVincent Cheng }
4343a6ba7dcSVincent Cheng 
4353a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm,
4363a6ba7dcSVincent Cheng 			    u8 pll,
4373a6ba7dcSVincent Cheng 			    u8 sync_src,
4383a6ba7dcSVincent Cheng 			    u8 qn,
4393a6ba7dcSVincent Cheng 			    u8 qn_plus_1)
4403a6ba7dcSVincent Cheng {
4413a6ba7dcSVincent Cheng 	int err;
4423a6ba7dcSVincent Cheng 	u8 val;
4433a6ba7dcSVincent Cheng 	u16 sync_ctrl0;
4443a6ba7dcSVincent Cheng 	u16 sync_ctrl1;
4457ea5fda2SMin Li 	u8 temp;
4463a6ba7dcSVincent Cheng 
4473a6ba7dcSVincent Cheng 	if ((qn == 0) && (qn_plus_1 == 0))
4483a6ba7dcSVincent Cheng 		return 0;
4493a6ba7dcSVincent Cheng 
4503a6ba7dcSVincent Cheng 	switch (pll) {
4513a6ba7dcSVincent Cheng 	case 0:
4523a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
4533a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
4543a6ba7dcSVincent Cheng 		break;
4553a6ba7dcSVincent Cheng 	case 1:
4563a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
4573a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
4583a6ba7dcSVincent Cheng 		break;
4593a6ba7dcSVincent Cheng 	case 2:
4603a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
4613a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
4623a6ba7dcSVincent Cheng 		break;
4633a6ba7dcSVincent Cheng 	case 3:
4643a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
4653a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
4663a6ba7dcSVincent Cheng 		break;
4673a6ba7dcSVincent Cheng 	case 4:
4683a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
4693a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
4703a6ba7dcSVincent Cheng 		break;
4713a6ba7dcSVincent Cheng 	case 5:
4723a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
4733a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
4743a6ba7dcSVincent Cheng 		break;
4753a6ba7dcSVincent Cheng 	case 6:
4763a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
4773a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
4783a6ba7dcSVincent Cheng 		break;
4793a6ba7dcSVincent Cheng 	case 7:
4803a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
4813a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
4823a6ba7dcSVincent Cheng 		break;
4833a6ba7dcSVincent Cheng 	default:
4843a6ba7dcSVincent Cheng 		return -EINVAL;
4853a6ba7dcSVincent Cheng 	}
4863a6ba7dcSVincent Cheng 
4873a6ba7dcSVincent Cheng 	val = SYNCTRL1_MASTER_SYNC_RST;
4883a6ba7dcSVincent Cheng 
4893a6ba7dcSVincent Cheng 	/* Place master sync in reset */
4903a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
4913a6ba7dcSVincent Cheng 	if (err)
4923a6ba7dcSVincent Cheng 		return err;
4933a6ba7dcSVincent Cheng 
4943a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
4953a6ba7dcSVincent Cheng 	if (err)
4963a6ba7dcSVincent Cheng 		return err;
4973a6ba7dcSVincent Cheng 
4983a6ba7dcSVincent Cheng 	/* Set sync trigger mask */
4993a6ba7dcSVincent Cheng 	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
5003a6ba7dcSVincent Cheng 
5013a6ba7dcSVincent Cheng 	if (qn)
5023a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
5033a6ba7dcSVincent Cheng 
5043a6ba7dcSVincent Cheng 	if (qn_plus_1)
5053a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
5063a6ba7dcSVincent Cheng 
5073a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5083a6ba7dcSVincent Cheng 	if (err)
5093a6ba7dcSVincent Cheng 		return err;
5103a6ba7dcSVincent Cheng 
5117ea5fda2SMin Li 	/* PLL5 can have OUT8 as second additional output. */
5127ea5fda2SMin Li 	if ((pll == 5) && (qn_plus_1 != 0)) {
5137ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
5147ea5fda2SMin Li 				 &temp, sizeof(temp));
5157ea5fda2SMin Li 		if (err)
5167ea5fda2SMin Li 			return err;
5177ea5fda2SMin Li 
5187ea5fda2SMin Li 		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
5197ea5fda2SMin Li 
5207ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5217ea5fda2SMin Li 				  &temp, sizeof(temp));
5227ea5fda2SMin Li 		if (err)
5237ea5fda2SMin Li 			return err;
5247ea5fda2SMin Li 
5257ea5fda2SMin Li 		temp |= Q9_TO_Q8_SYNC_TRIG;
5267ea5fda2SMin Li 
5277ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5287ea5fda2SMin Li 				  &temp, sizeof(temp));
5297ea5fda2SMin Li 		if (err)
5307ea5fda2SMin Li 			return err;
5317ea5fda2SMin Li 	}
5327ea5fda2SMin Li 
5337ea5fda2SMin Li 	/* PLL6 can have OUT11 as second additional output. */
5347ea5fda2SMin Li 	if ((pll == 6) && (qn_plus_1 != 0)) {
5357ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
5367ea5fda2SMin Li 				 &temp, sizeof(temp));
5377ea5fda2SMin Li 		if (err)
5387ea5fda2SMin Li 			return err;
5397ea5fda2SMin Li 
5407ea5fda2SMin Li 		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
5417ea5fda2SMin Li 
5427ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
5437ea5fda2SMin Li 				  &temp, sizeof(temp));
5447ea5fda2SMin Li 		if (err)
5457ea5fda2SMin Li 			return err;
5467ea5fda2SMin Li 
5477ea5fda2SMin Li 		temp |= Q10_TO_Q11_SYNC_TRIG;
5487ea5fda2SMin Li 
5497ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
5507ea5fda2SMin Li 				  &temp, sizeof(temp));
5517ea5fda2SMin Li 		if (err)
5527ea5fda2SMin Li 			return err;
5537ea5fda2SMin Li 	}
5547ea5fda2SMin Li 
5553a6ba7dcSVincent Cheng 	/* Place master sync out of reset */
5563a6ba7dcSVincent Cheng 	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
5573a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5583a6ba7dcSVincent Cheng 
5593a6ba7dcSVincent Cheng 	return err;
5603a6ba7dcSVincent Cheng }
5613a6ba7dcSVincent Cheng 
5627ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
5637ea5fda2SMin Li {
5647ea5fda2SMin Li 	int err = 0;
5657ea5fda2SMin Li 
5667ea5fda2SMin Li 	switch (tod_addr) {
5677ea5fda2SMin Li 	case TOD_0:
5687ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
5697ea5fda2SMin Li 		break;
5707ea5fda2SMin Li 	case TOD_1:
5717ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
5727ea5fda2SMin Li 		break;
5737ea5fda2SMin Li 	case TOD_2:
5747ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
5757ea5fda2SMin Li 		break;
5767ea5fda2SMin Li 	case TOD_3:
5777ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
5787ea5fda2SMin Li 		break;
5797ea5fda2SMin Li 	default:
5807ea5fda2SMin Li 		err = -EINVAL;
5817ea5fda2SMin Li 	}
5827ea5fda2SMin Li 
5837ea5fda2SMin Li 	return err;
5847ea5fda2SMin Li }
5857ea5fda2SMin Li 
5863a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel)
5873a6ba7dcSVincent Cheng {
5883a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
5893a6ba7dcSVincent Cheng 	u8 pll;
5903a6ba7dcSVincent Cheng 	u8 sync_src;
5913a6ba7dcSVincent Cheng 	u8 qn;
5923a6ba7dcSVincent Cheng 	u8 qn_plus_1;
5933a6ba7dcSVincent Cheng 	int err = 0;
5947ea5fda2SMin Li 	u8 out8_mux = 0;
5957ea5fda2SMin Li 	u8 out11_mux = 0;
5967ea5fda2SMin Li 	u8 temp;
5973a6ba7dcSVincent Cheng 	u16 output_mask = channel->output_mask;
5983a6ba7dcSVincent Cheng 
5997ea5fda2SMin Li 	err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
6007ea5fda2SMin Li 	if (err)
6017ea5fda2SMin Li 		return err;
6027ea5fda2SMin Li 
6037ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
6047ea5fda2SMin Li 			 &temp, sizeof(temp));
6057ea5fda2SMin Li 	if (err)
6067ea5fda2SMin Li 		return err;
6077ea5fda2SMin Li 
6087ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6097ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6107ea5fda2SMin Li 		out8_mux = 1;
6117ea5fda2SMin Li 
6127ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
6137ea5fda2SMin Li 			 &temp, sizeof(temp));
6147ea5fda2SMin Li 	if (err)
6157ea5fda2SMin Li 		return err;
6167ea5fda2SMin Li 
6177ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6187ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6197ea5fda2SMin Li 		out11_mux = 1;
6203a6ba7dcSVincent Cheng 
6213a6ba7dcSVincent Cheng 	for (pll = 0; pll < 8; pll++) {
6227ea5fda2SMin Li 		qn = 0;
6237ea5fda2SMin Li 		qn_plus_1 = 0;
6243a6ba7dcSVincent Cheng 
6253a6ba7dcSVincent Cheng 		if (pll < 4) {
6263a6ba7dcSVincent Cheng 			/* First 4 pll has 2 outputs */
6277ea5fda2SMin Li 			qn = output_mask & 0x1;
6287ea5fda2SMin Li 			output_mask = output_mask >> 1;
6293a6ba7dcSVincent Cheng 			qn_plus_1 = output_mask & 0x1;
6303a6ba7dcSVincent Cheng 			output_mask = output_mask >> 1;
6317ea5fda2SMin Li 		} else if (pll == 4) {
6327ea5fda2SMin Li 			if (out8_mux == 0) {
6337ea5fda2SMin Li 				qn = output_mask & 0x1;
6347ea5fda2SMin Li 				output_mask = output_mask >> 1;
6357ea5fda2SMin Li 			}
6367ea5fda2SMin Li 		} else if (pll == 5) {
6377ea5fda2SMin Li 			if (out8_mux) {
6387ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6397ea5fda2SMin Li 				output_mask = output_mask >> 1;
6407ea5fda2SMin Li 			}
6417ea5fda2SMin Li 			qn = output_mask & 0x1;
6427ea5fda2SMin Li 			output_mask = output_mask >> 1;
6437ea5fda2SMin Li 		} else if (pll == 6) {
6447ea5fda2SMin Li 			qn = output_mask & 0x1;
6457ea5fda2SMin Li 			output_mask = output_mask >> 1;
6467ea5fda2SMin Li 			if (out11_mux) {
6477ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6487ea5fda2SMin Li 				output_mask = output_mask >> 1;
6497ea5fda2SMin Li 			}
6507ea5fda2SMin Li 		} else if (pll == 7) {
6517ea5fda2SMin Li 			if (out11_mux == 0) {
6527ea5fda2SMin Li 				qn = output_mask & 0x1;
6537ea5fda2SMin Li 				output_mask = output_mask >> 1;
6547ea5fda2SMin Li 			}
6553a6ba7dcSVincent Cheng 		}
6563a6ba7dcSVincent Cheng 
6573a6ba7dcSVincent Cheng 		if ((qn != 0) || (qn_plus_1 != 0))
6583a6ba7dcSVincent Cheng 			err = _sync_pll_output(idtcm, pll, sync_src, qn,
6593a6ba7dcSVincent Cheng 					       qn_plus_1);
6603a6ba7dcSVincent Cheng 
6613a6ba7dcSVincent Cheng 		if (err)
6623a6ba7dcSVincent Cheng 			return err;
6633a6ba7dcSVincent Cheng 	}
6643a6ba7dcSVincent Cheng 
6653a6ba7dcSVincent Cheng 	return err;
6663a6ba7dcSVincent Cheng }
6673a6ba7dcSVincent Cheng 
6687ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
6693a6ba7dcSVincent Cheng 			       struct timespec64 const *ts,
6703a6ba7dcSVincent Cheng 			       enum hw_tod_write_trig_sel wr_trig)
6713a6ba7dcSVincent Cheng {
6723a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
6733a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
6743a6ba7dcSVincent Cheng 	u8 cmd;
6753a6ba7dcSVincent Cheng 	int err;
6763a6ba7dcSVincent Cheng 	struct timespec64 local_ts = *ts;
6773a6ba7dcSVincent Cheng 	s64 total_overhead_ns;
6783a6ba7dcSVincent Cheng 
6793a6ba7dcSVincent Cheng 	/* Configure HW TOD write trigger. */
6803a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6813a6ba7dcSVincent Cheng 			 &cmd, sizeof(cmd));
6823a6ba7dcSVincent Cheng 	if (err)
6833a6ba7dcSVincent Cheng 		return err;
6843a6ba7dcSVincent Cheng 
6853a6ba7dcSVincent Cheng 	cmd &= ~(0x0f);
6863a6ba7dcSVincent Cheng 	cmd |= wr_trig | 0x08;
6873a6ba7dcSVincent Cheng 
6883a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6893a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
6903a6ba7dcSVincent Cheng 	if (err)
6913a6ba7dcSVincent Cheng 		return err;
6923a6ba7dcSVincent Cheng 
6933a6ba7dcSVincent Cheng 	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
6943a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6953a6ba7dcSVincent Cheng 		if (err)
6963a6ba7dcSVincent Cheng 			return err;
6973a6ba7dcSVincent Cheng 
6983a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
6993a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7003a6ba7dcSVincent Cheng 		if (err)
7013a6ba7dcSVincent Cheng 			return err;
7023a6ba7dcSVincent Cheng 	}
7033a6ba7dcSVincent Cheng 
7043a6ba7dcSVincent Cheng 	/* ARM HW TOD write trigger. */
7053a6ba7dcSVincent Cheng 	cmd &= ~(0x08);
7063a6ba7dcSVincent Cheng 
7073a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
7083a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
7093a6ba7dcSVincent Cheng 
7103a6ba7dcSVincent Cheng 	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
7113a6ba7dcSVincent Cheng 		if (idtcm->calculate_overhead_flag) {
7121ece2fbeSVincent Cheng 			/* Assumption: I2C @ 400KHz */
7137260d1c8SMin Li 			ktime_t diff = ktime_sub(ktime_get_raw(),
7147260d1c8SMin Li 						 idtcm->start_time);
7157260d1c8SMin Li 			total_overhead_ns =  ktime_to_ns(diff)
7163a6ba7dcSVincent Cheng 					     + idtcm->tod_write_overhead_ns
7173a6ba7dcSVincent Cheng 					     + SETTIME_CORRECTION;
7183a6ba7dcSVincent Cheng 
7193a6ba7dcSVincent Cheng 			timespec64_add_ns(&local_ts, total_overhead_ns);
7203a6ba7dcSVincent Cheng 
7213a6ba7dcSVincent Cheng 			idtcm->calculate_overhead_flag = 0;
7223a6ba7dcSVincent Cheng 		}
7233a6ba7dcSVincent Cheng 
7243a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7253a6ba7dcSVincent Cheng 		if (err)
7263a6ba7dcSVincent Cheng 			return err;
7273a6ba7dcSVincent Cheng 
7283a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
7293a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7303a6ba7dcSVincent Cheng 	}
7313a6ba7dcSVincent Cheng 
7323a6ba7dcSVincent Cheng 	return err;
7333a6ba7dcSVincent Cheng }
7343a6ba7dcSVincent Cheng 
7357ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
7367ea5fda2SMin Li 				    struct timespec64 const *ts,
7377ea5fda2SMin Li 				    enum scsr_tod_write_trig_sel wr_trig,
7387ea5fda2SMin Li 				    enum scsr_tod_write_type_sel wr_type)
7397ea5fda2SMin Li {
7407ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
7417ea5fda2SMin Li 	unsigned char buf[TOD_BYTE_COUNT], cmd;
7427ea5fda2SMin Li 	struct timespec64 local_ts = *ts;
7437ea5fda2SMin Li 	int err, count = 0;
7447ea5fda2SMin Li 
7457ea5fda2SMin Li 	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
7467ea5fda2SMin Li 
7477ea5fda2SMin Li 	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7487ea5fda2SMin Li 	if (err)
7497ea5fda2SMin Li 		return err;
7507ea5fda2SMin Li 
7517ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
7527ea5fda2SMin Li 			  buf, sizeof(buf));
7537ea5fda2SMin Li 	if (err)
7547ea5fda2SMin Li 		return err;
7557ea5fda2SMin Li 
7567ea5fda2SMin Li 	/* Trigger the write operation. */
7577ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7587ea5fda2SMin Li 			 &cmd, sizeof(cmd));
7597ea5fda2SMin Li 	if (err)
7607ea5fda2SMin Li 		return err;
7617ea5fda2SMin Li 
7627ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
7637ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
7647ea5fda2SMin Li 	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
7657ea5fda2SMin Li 	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
7667ea5fda2SMin Li 
7677ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
7687ea5fda2SMin Li 			   &cmd, sizeof(cmd));
7697ea5fda2SMin Li 	if (err)
7707ea5fda2SMin Li 		return err;
7717ea5fda2SMin Li 
7727ea5fda2SMin Li 	/* Wait for the operation to complete. */
7737ea5fda2SMin Li 	while (1) {
7747ea5fda2SMin Li 		/* pps trigger takes up to 1 sec to complete */
7757ea5fda2SMin Li 		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
7767ea5fda2SMin Li 			msleep(50);
7777ea5fda2SMin Li 
7787ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7797ea5fda2SMin Li 				 &cmd, sizeof(cmd));
7807ea5fda2SMin Li 		if (err)
7817ea5fda2SMin Li 			return err;
7827ea5fda2SMin Li 
783251f4fe2SMin Li 		if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
7847ea5fda2SMin Li 			break;
7857ea5fda2SMin Li 
7867ea5fda2SMin Li 		if (++count > 20) {
7877ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
7881c49d3e9SVincent Cheng 				"Timed out waiting for the write counter");
7897ea5fda2SMin Li 			return -EIO;
7907ea5fda2SMin Li 		}
7917ea5fda2SMin Li 	}
7927ea5fda2SMin Li 
7937ea5fda2SMin Li 	return 0;
7947ea5fda2SMin Li }
7957ea5fda2SMin Li 
7967260d1c8SMin Li static int get_output_base_addr(u8 outn)
7977260d1c8SMin Li {
7987260d1c8SMin Li 	int base;
7997260d1c8SMin Li 
8007260d1c8SMin Li 	switch (outn) {
8017260d1c8SMin Li 	case 0:
8027260d1c8SMin Li 		base = OUTPUT_0;
8037260d1c8SMin Li 		break;
8047260d1c8SMin Li 	case 1:
8057260d1c8SMin Li 		base = OUTPUT_1;
8067260d1c8SMin Li 		break;
8077260d1c8SMin Li 	case 2:
8087260d1c8SMin Li 		base = OUTPUT_2;
8097260d1c8SMin Li 		break;
8107260d1c8SMin Li 	case 3:
8117260d1c8SMin Li 		base = OUTPUT_3;
8127260d1c8SMin Li 		break;
8137260d1c8SMin Li 	case 4:
8147260d1c8SMin Li 		base = OUTPUT_4;
8157260d1c8SMin Li 		break;
8167260d1c8SMin Li 	case 5:
8177260d1c8SMin Li 		base = OUTPUT_5;
8187260d1c8SMin Li 		break;
8197260d1c8SMin Li 	case 6:
8207260d1c8SMin Li 		base = OUTPUT_6;
8217260d1c8SMin Li 		break;
8227260d1c8SMin Li 	case 7:
8237260d1c8SMin Li 		base = OUTPUT_7;
8247260d1c8SMin Li 		break;
8257260d1c8SMin Li 	case 8:
8267260d1c8SMin Li 		base = OUTPUT_8;
8277260d1c8SMin Li 		break;
8287260d1c8SMin Li 	case 9:
8297260d1c8SMin Li 		base = OUTPUT_9;
8307260d1c8SMin Li 		break;
8317260d1c8SMin Li 	case 10:
8327260d1c8SMin Li 		base = OUTPUT_10;
8337260d1c8SMin Li 		break;
8347260d1c8SMin Li 	case 11:
8357260d1c8SMin Li 		base = OUTPUT_11;
8367260d1c8SMin Li 		break;
8377260d1c8SMin Li 	default:
8387260d1c8SMin Li 		base = -EINVAL;
8397260d1c8SMin Li 	}
8407260d1c8SMin Li 
8417260d1c8SMin Li 	return base;
8427260d1c8SMin Li }
8437260d1c8SMin Li 
844da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
845251f4fe2SMin Li 				     struct timespec64 const *ts)
8463a6ba7dcSVincent Cheng {
8473a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8483a6ba7dcSVincent Cheng 	int err;
8493a6ba7dcSVincent Cheng 
850251f4fe2SMin Li 	err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
8517ea5fda2SMin Li 	if (err) {
8527ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
8531c49d3e9SVincent Cheng 			"%s: Set HW ToD failed", __func__);
8543a6ba7dcSVincent Cheng 		return err;
8557ea5fda2SMin Li 	}
8563a6ba7dcSVincent Cheng 
8577ea5fda2SMin Li 	return idtcm_sync_pps_output(channel);
8587ea5fda2SMin Li }
8593a6ba7dcSVincent Cheng 
860da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel,
8617ea5fda2SMin Li 			  struct timespec64 const *ts,
8627ea5fda2SMin Li 			  enum scsr_tod_write_type_sel wr_type)
8637ea5fda2SMin Li {
8647ea5fda2SMin Li 	return _idtcm_set_dpll_scsr_tod(channel, ts,
8657ea5fda2SMin Li 					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
8667ea5fda2SMin Li 					wr_type);
8673a6ba7dcSVincent Cheng }
8683a6ba7dcSVincent Cheng 
8693a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
8703a6ba7dcSVincent Cheng 					  s32 offset_ns)
8713a6ba7dcSVincent Cheng {
8723a6ba7dcSVincent Cheng 	int err;
8733a6ba7dcSVincent Cheng 	int i;
8743a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8753a6ba7dcSVincent Cheng 	u8 buf[4];
8763a6ba7dcSVincent Cheng 
8773a6ba7dcSVincent Cheng 	for (i = 0; i < 4; i++) {
8783a6ba7dcSVincent Cheng 		buf[i] = 0xff & (offset_ns);
8793a6ba7dcSVincent Cheng 		offset_ns >>= 8;
8803a6ba7dcSVincent Cheng 	}
8813a6ba7dcSVincent Cheng 
8823a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
8833a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
8843a6ba7dcSVincent Cheng 
8853a6ba7dcSVincent Cheng 	return err;
8863a6ba7dcSVincent Cheng }
8873a6ba7dcSVincent Cheng 
8883a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
8893a6ba7dcSVincent Cheng 					       u32 max_ffo_ppb)
8903a6ba7dcSVincent Cheng {
8913a6ba7dcSVincent Cheng 	int err;
8923a6ba7dcSVincent Cheng 	u8 i;
8933a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8943a6ba7dcSVincent Cheng 	u8 buf[3];
8953a6ba7dcSVincent Cheng 
8963a6ba7dcSVincent Cheng 	if (max_ffo_ppb & 0xff000000)
8973a6ba7dcSVincent Cheng 		max_ffo_ppb = 0;
8983a6ba7dcSVincent Cheng 
8993a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
9003a6ba7dcSVincent Cheng 		buf[i] = 0xff & (max_ffo_ppb);
9013a6ba7dcSVincent Cheng 		max_ffo_ppb >>= 8;
9023a6ba7dcSVincent Cheng 	}
9033a6ba7dcSVincent Cheng 
9043a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9053a6ba7dcSVincent Cheng 			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
9063a6ba7dcSVincent Cheng 
9073a6ba7dcSVincent Cheng 	return err;
9083a6ba7dcSVincent Cheng }
9093a6ba7dcSVincent Cheng 
9103a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
9113a6ba7dcSVincent Cheng {
9123a6ba7dcSVincent Cheng 	int err;
9133a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9143a6ba7dcSVincent Cheng 	u8 buf;
9153a6ba7dcSVincent Cheng 
9163a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
9173a6ba7dcSVincent Cheng 			 &buf, sizeof(buf));
9183a6ba7dcSVincent Cheng 	if (err)
9193a6ba7dcSVincent Cheng 		return err;
9203a6ba7dcSVincent Cheng 
9213a6ba7dcSVincent Cheng 	if (buf == 0) {
9223a6ba7dcSVincent Cheng 		buf = 0x01;
9233a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9243a6ba7dcSVincent Cheng 				  PULL_IN_CTRL, &buf, sizeof(buf));
9253a6ba7dcSVincent Cheng 	} else {
9263a6ba7dcSVincent Cheng 		err = -EBUSY;
9273a6ba7dcSVincent Cheng 	}
9283a6ba7dcSVincent Cheng 
9293a6ba7dcSVincent Cheng 	return err;
9303a6ba7dcSVincent Cheng }
9313a6ba7dcSVincent Cheng 
9323a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
9333a6ba7dcSVincent Cheng 				  s32 offset_ns,
9343a6ba7dcSVincent Cheng 				  u32 max_ffo_ppb)
9353a6ba7dcSVincent Cheng {
9363a6ba7dcSVincent Cheng 	int err;
9373a6ba7dcSVincent Cheng 
9383a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
9393a6ba7dcSVincent Cheng 	if (err)
9403a6ba7dcSVincent Cheng 		return err;
9413a6ba7dcSVincent Cheng 
9423a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
9433a6ba7dcSVincent Cheng 	if (err)
9443a6ba7dcSVincent Cheng 		return err;
9453a6ba7dcSVincent Cheng 
9463a6ba7dcSVincent Cheng 	err = idtcm_start_phase_pull_in(channel);
9473a6ba7dcSVincent Cheng 
9483a6ba7dcSVincent Cheng 	return err;
9493a6ba7dcSVincent Cheng }
9503a6ba7dcSVincent Cheng 
9517ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel)
9527ea5fda2SMin Li {
9537ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
9547ea5fda2SMin Li 	s64 current_ns = 0;
9557ea5fda2SMin Li 	s64 lowest_ns = 0;
9567ea5fda2SMin Li 	int err;
9577ea5fda2SMin Li 	u8 i;
9587ea5fda2SMin Li 	ktime_t start;
9597ea5fda2SMin Li 	ktime_t stop;
9607260d1c8SMin Li 	ktime_t diff;
9617ea5fda2SMin Li 
9627ea5fda2SMin Li 	char buf[TOD_BYTE_COUNT] = {0};
9637ea5fda2SMin Li 
9647ea5fda2SMin Li 	/* Set page offset */
9657ea5fda2SMin Li 	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
9667ea5fda2SMin Li 		    buf, sizeof(buf));
9677ea5fda2SMin Li 
9687ea5fda2SMin Li 	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
9697ea5fda2SMin Li 		start = ktime_get_raw();
9707ea5fda2SMin Li 
9717ea5fda2SMin Li 		err = idtcm_write(idtcm, channel->hw_dpll_n,
9727ea5fda2SMin Li 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
9737ea5fda2SMin Li 		if (err)
9747ea5fda2SMin Li 			return err;
9757ea5fda2SMin Li 
9767ea5fda2SMin Li 		stop = ktime_get_raw();
9777ea5fda2SMin Li 
9787260d1c8SMin Li 		diff = ktime_sub(stop, start);
9797260d1c8SMin Li 
9807260d1c8SMin Li 		current_ns = ktime_to_ns(diff);
9817ea5fda2SMin Li 
9827ea5fda2SMin Li 		if (i == 0) {
9837ea5fda2SMin Li 			lowest_ns = current_ns;
9847ea5fda2SMin Li 		} else {
9857ea5fda2SMin Li 			if (current_ns < lowest_ns)
9867ea5fda2SMin Li 				lowest_ns = current_ns;
9877ea5fda2SMin Li 		}
9887ea5fda2SMin Li 	}
9897ea5fda2SMin Li 
9907ea5fda2SMin Li 	idtcm->tod_write_overhead_ns = lowest_ns;
9917ea5fda2SMin Li 
9927ea5fda2SMin Li 	return err;
9937ea5fda2SMin Li }
9947ea5fda2SMin Li 
995da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
9963a6ba7dcSVincent Cheng {
9973a6ba7dcSVincent Cheng 	int err;
9983a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9993a6ba7dcSVincent Cheng 	struct timespec64 ts;
10003a6ba7dcSVincent Cheng 	s64 now;
10013a6ba7dcSVincent Cheng 
1002da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
10033a6ba7dcSVincent Cheng 		err = idtcm_do_phase_pull_in(channel, delta, 0);
10043a6ba7dcSVincent Cheng 	} else {
10053a6ba7dcSVincent Cheng 		idtcm->calculate_overhead_flag = 1;
10063a6ba7dcSVincent Cheng 
10077ea5fda2SMin Li 		err = set_tod_write_overhead(channel);
10087ea5fda2SMin Li 		if (err)
10097ea5fda2SMin Li 			return err;
10107ea5fda2SMin Li 
10113a6ba7dcSVincent Cheng 		err = _idtcm_gettime(channel, &ts);
10123a6ba7dcSVincent Cheng 		if (err)
10133a6ba7dcSVincent Cheng 			return err;
10143a6ba7dcSVincent Cheng 
10153a6ba7dcSVincent Cheng 		now = timespec64_to_ns(&ts);
10163a6ba7dcSVincent Cheng 		now += delta;
10173a6ba7dcSVincent Cheng 
10183a6ba7dcSVincent Cheng 		ts = ns_to_timespec64(now);
10193a6ba7dcSVincent Cheng 
1020da948233SMin Li 		err = _idtcm_settime_deprecated(channel, &ts);
10213a6ba7dcSVincent Cheng 	}
10223a6ba7dcSVincent Cheng 
10233a6ba7dcSVincent Cheng 	return err;
10243a6ba7dcSVincent Cheng }
10253a6ba7dcSVincent Cheng 
10263a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm)
10273a6ba7dcSVincent Cheng {
10283a6ba7dcSVincent Cheng 	u8 byte = SM_RESET_CMD;
1029251f4fe2SMin Li 	u32 status = 0;
1030251f4fe2SMin Li 	int err;
1031251f4fe2SMin Li 	u8 i;
1032251f4fe2SMin Li 
1033251f4fe2SMin Li 	clear_boot_status(idtcm);
10343a6ba7dcSVincent Cheng 
10353a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
10363a6ba7dcSVincent Cheng 
1037251f4fe2SMin Li 	if (!err) {
1038251f4fe2SMin Li 		for (i = 0; i < 30; i++) {
1039251f4fe2SMin Li 			msleep_interruptible(100);
1040251f4fe2SMin Li 			read_boot_status(idtcm, &status);
1041251f4fe2SMin Li 
1042251f4fe2SMin Li 			if (status == 0xA0) {
1043251f4fe2SMin Li 				dev_dbg(&idtcm->client->dev,
10441c49d3e9SVincent Cheng 					"SM_RESET completed in %d ms", i * 100);
1045251f4fe2SMin Li 				break;
1046251f4fe2SMin Li 			}
1047251f4fe2SMin Li 		}
1048251f4fe2SMin Li 
1049251f4fe2SMin Li 		if (!status)
10501c49d3e9SVincent Cheng 			dev_err(&idtcm->client->dev,
10511c49d3e9SVincent Cheng 				"Timed out waiting for CM_RESET to complete");
1052251f4fe2SMin Li 	}
10533a6ba7dcSVincent Cheng 
10543a6ba7dcSVincent Cheng 	return err;
10553a6ba7dcSVincent Cheng }
10563a6ba7dcSVincent Cheng 
10573a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
10583a6ba7dcSVincent Cheng {
10591ece2fbeSVincent Cheng 	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
10603a6ba7dcSVincent Cheng }
10613a6ba7dcSVincent Cheng 
10623a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
10633a6ba7dcSVincent Cheng {
10643a6ba7dcSVincent Cheng 	int err;
10653a6ba7dcSVincent Cheng 	u8 buf[2] = {0};
10663a6ba7dcSVincent Cheng 
10673a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
10683a6ba7dcSVincent Cheng 
10693a6ba7dcSVincent Cheng 	*product_id = (buf[1] << 8) | buf[0];
10703a6ba7dcSVincent Cheng 
10713a6ba7dcSVincent Cheng 	return err;
10723a6ba7dcSVincent Cheng }
10733a6ba7dcSVincent Cheng 
10743a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
10753a6ba7dcSVincent Cheng {
10763a6ba7dcSVincent Cheng 	int err;
10773a6ba7dcSVincent Cheng 	u8 buf = 0;
10783a6ba7dcSVincent Cheng 
10793a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
10803a6ba7dcSVincent Cheng 
10813a6ba7dcSVincent Cheng 	*major = buf >> 1;
10823a6ba7dcSVincent Cheng 
10833a6ba7dcSVincent Cheng 	return err;
10843a6ba7dcSVincent Cheng }
10853a6ba7dcSVincent Cheng 
10863a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
10873a6ba7dcSVincent Cheng {
10883a6ba7dcSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
10893a6ba7dcSVincent Cheng }
10903a6ba7dcSVincent Cheng 
10913a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
10923a6ba7dcSVincent Cheng {
10933a6ba7dcSVincent Cheng 	return idtcm_read(idtcm,
10943a6ba7dcSVincent Cheng 			  GENERAL_STATUS,
10953a6ba7dcSVincent Cheng 			  HOTFIX_REL,
10963a6ba7dcSVincent Cheng 			  hotfix,
10973a6ba7dcSVincent Cheng 			  sizeof(u8));
10983a6ba7dcSVincent Cheng }
10993a6ba7dcSVincent Cheng 
11001ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
11011ece2fbeSVincent Cheng 					     u8 *config_select)
11023a6ba7dcSVincent Cheng {
11031ece2fbeSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
11041ece2fbeSVincent Cheng 			  config_select, sizeof(u8));
11053a6ba7dcSVincent Cheng }
11063a6ba7dcSVincent Cheng 
11073a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
11083a6ba7dcSVincent Cheng {
11093a6ba7dcSVincent Cheng 	int err = 0;
11103a6ba7dcSVincent Cheng 
11113a6ba7dcSVincent Cheng 	switch (addr) {
11127ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR:
11133a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[0].output_mask, val);
11143a6ba7dcSVincent Cheng 		break;
11157ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
11163a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[0].output_mask, val);
11173a6ba7dcSVincent Cheng 		break;
11187ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR:
11193a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[1].output_mask, val);
11203a6ba7dcSVincent Cheng 		break;
11217ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
11223a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[1].output_mask, val);
11233a6ba7dcSVincent Cheng 		break;
11247ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR:
11253a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[2].output_mask, val);
11263a6ba7dcSVincent Cheng 		break;
11277ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
11283a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[2].output_mask, val);
11293a6ba7dcSVincent Cheng 		break;
11307ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR:
11313a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[3].output_mask, val);
11323a6ba7dcSVincent Cheng 		break;
11337ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
11343a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[3].output_mask, val);
11353a6ba7dcSVincent Cheng 		break;
11363a6ba7dcSVincent Cheng 	default:
11377ea5fda2SMin Li 		err = -EFAULT; /* Bad address */;
11383a6ba7dcSVincent Cheng 		break;
11393a6ba7dcSVincent Cheng 	}
11403a6ba7dcSVincent Cheng 
11413a6ba7dcSVincent Cheng 	return err;
11423a6ba7dcSVincent Cheng }
11433a6ba7dcSVincent Cheng 
11447ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
11457ea5fda2SMin Li {
11467ea5fda2SMin Li 	if (index >= MAX_TOD) {
11471c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "ToD%d not supported", index);
11487ea5fda2SMin Li 		return -EINVAL;
11497ea5fda2SMin Li 	}
11507ea5fda2SMin Li 
11517ea5fda2SMin Li 	if (pll >= MAX_PLL) {
11521c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "Pll%d not supported", pll);
11537ea5fda2SMin Li 		return -EINVAL;
11547ea5fda2SMin Li 	}
11557ea5fda2SMin Li 
11567ea5fda2SMin Li 	idtcm->channel[index].pll = pll;
11577ea5fda2SMin Li 
11587ea5fda2SMin Li 	return 0;
11597ea5fda2SMin Li }
11607ea5fda2SMin Li 
11613a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm,
11623a6ba7dcSVincent Cheng 			       u16 regaddr,
11633a6ba7dcSVincent Cheng 			       u8 val)
11643a6ba7dcSVincent Cheng {
11653a6ba7dcSVincent Cheng 	int err = 0;
11663a6ba7dcSVincent Cheng 
11677ea5fda2SMin Li 	switch (regaddr) {
11687ea5fda2SMin Li 	case TOD_MASK_ADDR:
11697ea5fda2SMin Li 		if ((val & 0xf0) || !(val & 0x0f)) {
11701c49d3e9SVincent Cheng 			dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val);
11717ea5fda2SMin Li 			err = -EINVAL;
11727ea5fda2SMin Li 		} else {
11737ea5fda2SMin Li 			idtcm->tod_mask = val;
11747ea5fda2SMin Li 		}
11757ea5fda2SMin Li 		break;
11767ea5fda2SMin Li 	case TOD0_PTP_PLL_ADDR:
11777ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 0, val);
11787ea5fda2SMin Li 		break;
11797ea5fda2SMin Li 	case TOD1_PTP_PLL_ADDR:
11807ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 1, val);
11817ea5fda2SMin Li 		break;
11827ea5fda2SMin Li 	case TOD2_PTP_PLL_ADDR:
11837ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 2, val);
11847ea5fda2SMin Li 		break;
11857ea5fda2SMin Li 	case TOD3_PTP_PLL_ADDR:
11867ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 3, val);
11877ea5fda2SMin Li 		break;
11887ea5fda2SMin Li 	default:
11897ea5fda2SMin Li 		err = set_pll_output_mask(idtcm, regaddr, val);
11907ea5fda2SMin Li 		break;
11913a6ba7dcSVincent Cheng 	}
11923a6ba7dcSVincent Cheng 
11933a6ba7dcSVincent Cheng 	return err;
11943a6ba7dcSVincent Cheng }
11953a6ba7dcSVincent Cheng 
11967ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm)
11973a6ba7dcSVincent Cheng {
11983a6ba7dcSVincent Cheng 	u8 i;
11993a6ba7dcSVincent Cheng 	u8 mask;
12003a6ba7dcSVincent Cheng 
12011c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask);
12023a6ba7dcSVincent Cheng 
12037ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
12043a6ba7dcSVincent Cheng 		mask = 1 << i;
12053a6ba7dcSVincent Cheng 
12067ea5fda2SMin Li 		if (mask & idtcm->tod_mask)
12073a6ba7dcSVincent Cheng 			dev_dbg(&idtcm->client->dev,
12081c49d3e9SVincent Cheng 				"TOD%d pll = %d    output_mask = 0x%04x",
12097ea5fda2SMin Li 				i, idtcm->channel[i].pll,
12107ea5fda2SMin Li 				idtcm->channel[i].output_mask);
12113a6ba7dcSVincent Cheng 	}
12123a6ba7dcSVincent Cheng }
12133a6ba7dcSVincent Cheng 
12143a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm,
12153a6ba7dcSVincent Cheng 			       struct device *dev)
12163a6ba7dcSVincent Cheng {
12177ea5fda2SMin Li 	char fname[128] = FW_FILENAME;
12183a6ba7dcSVincent Cheng 	const struct firmware *fw;
12193a6ba7dcSVincent Cheng 	struct idtcm_fwrc *rec;
12203a6ba7dcSVincent Cheng 	u32 regaddr;
12213a6ba7dcSVincent Cheng 	int err;
12223a6ba7dcSVincent Cheng 	s32 len;
12233a6ba7dcSVincent Cheng 	u8 val;
12243a6ba7dcSVincent Cheng 	u8 loaddr;
12253a6ba7dcSVincent Cheng 
12267ea5fda2SMin Li 	if (firmware) /* module parameter */
12277ea5fda2SMin Li 		snprintf(fname, sizeof(fname), "%s", firmware);
12283a6ba7dcSVincent Cheng 
12291c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname);
12303a6ba7dcSVincent Cheng 
12317ea5fda2SMin Li 	err = request_firmware(&fw, fname, dev);
12327ea5fda2SMin Li 	if (err) {
12337ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
12341c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
12353a6ba7dcSVincent Cheng 		return err;
12367ea5fda2SMin Li 	}
12373a6ba7dcSVincent Cheng 
12381c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size);
12393a6ba7dcSVincent Cheng 
12403a6ba7dcSVincent Cheng 	rec = (struct idtcm_fwrc *) fw->data;
12413a6ba7dcSVincent Cheng 
1242251f4fe2SMin Li 	if (contains_full_configuration(fw))
12433a6ba7dcSVincent Cheng 		idtcm_state_machine_reset(idtcm);
12443a6ba7dcSVincent Cheng 
12453a6ba7dcSVincent Cheng 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
12463a6ba7dcSVincent Cheng 		if (rec->reserved) {
12473a6ba7dcSVincent Cheng 			dev_err(&idtcm->client->dev,
12481c49d3e9SVincent Cheng 				"bad firmware, reserved field non-zero");
12493a6ba7dcSVincent Cheng 			err = -EINVAL;
12503a6ba7dcSVincent Cheng 		} else {
12513a6ba7dcSVincent Cheng 			regaddr = rec->hiaddr << 8;
12523a6ba7dcSVincent Cheng 			regaddr |= rec->loaddr;
12533a6ba7dcSVincent Cheng 
12543a6ba7dcSVincent Cheng 			val = rec->value;
12553a6ba7dcSVincent Cheng 			loaddr = rec->loaddr;
12563a6ba7dcSVincent Cheng 
12573a6ba7dcSVincent Cheng 			rec++;
12583a6ba7dcSVincent Cheng 
12593a6ba7dcSVincent Cheng 			err = check_and_set_masks(idtcm, regaddr, val);
12603a6ba7dcSVincent Cheng 		}
12613a6ba7dcSVincent Cheng 
12627ea5fda2SMin Li 		if (err != -EINVAL) {
12637ea5fda2SMin Li 			err = 0;
12647ea5fda2SMin Li 
12653a6ba7dcSVincent Cheng 			/* Top (status registers) and bottom are read-only */
12663a6ba7dcSVincent Cheng 			if ((regaddr < GPIO_USER_CONTROL)
12673a6ba7dcSVincent Cheng 			    || (regaddr >= SCRATCH))
12683a6ba7dcSVincent Cheng 				continue;
12693a6ba7dcSVincent Cheng 
12703a6ba7dcSVincent Cheng 			/* Page size 128, last 4 bytes of page skipped */
12713a6ba7dcSVincent Cheng 			if (((loaddr > 0x7b) && (loaddr <= 0x7f))
12723e14462fSYang Yingliang 			     || loaddr > 0xfb)
12733a6ba7dcSVincent Cheng 				continue;
12743a6ba7dcSVincent Cheng 
12753a6ba7dcSVincent Cheng 			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
12763a6ba7dcSVincent Cheng 		}
12773a6ba7dcSVincent Cheng 
12783a6ba7dcSVincent Cheng 		if (err)
12793a6ba7dcSVincent Cheng 			goto out;
12803a6ba7dcSVincent Cheng 	}
12813a6ba7dcSVincent Cheng 
12827ea5fda2SMin Li 	display_pll_and_masks(idtcm);
12833a6ba7dcSVincent Cheng 
12843a6ba7dcSVincent Cheng out:
12853a6ba7dcSVincent Cheng 	release_firmware(fw);
12863a6ba7dcSVincent Cheng 	return err;
12873a6ba7dcSVincent Cheng }
12883a6ba7dcSVincent Cheng 
12897ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel,
12907ea5fda2SMin Li 			       bool enable, unsigned int outn)
12913a6ba7dcSVincent Cheng {
12923a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
12937260d1c8SMin Li 	int base;
12943a6ba7dcSVincent Cheng 	int err;
12957ea5fda2SMin Li 	u8 val;
12963a6ba7dcSVincent Cheng 
12977260d1c8SMin Li 	base = get_output_base_addr(outn);
12987260d1c8SMin Li 
12997260d1c8SMin Li 	if (!(base > 0)) {
13007260d1c8SMin Li 		dev_err(&idtcm->client->dev,
13017260d1c8SMin Li 			"%s - Unsupported out%d", __func__, outn);
13027260d1c8SMin Li 		return base;
13037260d1c8SMin Li 	}
13047260d1c8SMin Li 
13057260d1c8SMin Li 	err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13063a6ba7dcSVincent Cheng 	if (err)
13073a6ba7dcSVincent Cheng 		return err;
13083a6ba7dcSVincent Cheng 
13093a6ba7dcSVincent Cheng 	if (enable)
13103a6ba7dcSVincent Cheng 		val |= SQUELCH_DISABLE;
13113a6ba7dcSVincent Cheng 	else
13123a6ba7dcSVincent Cheng 		val &= ~SQUELCH_DISABLE;
13133a6ba7dcSVincent Cheng 
13147260d1c8SMin Li 	return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13157ea5fda2SMin Li }
13167ea5fda2SMin Li 
13177ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel,
13187ea5fda2SMin Li 				    bool enable)
13197ea5fda2SMin Li {
13207ea5fda2SMin Li 	u16 mask;
13217ea5fda2SMin Li 	int err;
13227ea5fda2SMin Li 	u8 outn;
13237ea5fda2SMin Li 
13247ea5fda2SMin Li 	mask = channel->output_mask;
13257ea5fda2SMin Li 	outn = 0;
13267ea5fda2SMin Li 
13277ea5fda2SMin Li 	while (mask) {
13287ea5fda2SMin Li 		if (mask & 0x1) {
13297ea5fda2SMin Li 			err = idtcm_output_enable(channel, enable, outn);
13303a6ba7dcSVincent Cheng 			if (err)
13313a6ba7dcSVincent Cheng 				return err;
13327ea5fda2SMin Li 		}
13337ea5fda2SMin Li 
13347ea5fda2SMin Li 		mask >>= 0x1;
13357ea5fda2SMin Li 		outn++;
13367ea5fda2SMin Li 	}
13373a6ba7dcSVincent Cheng 
13383a6ba7dcSVincent Cheng 	return 0;
13393a6ba7dcSVincent Cheng }
13403a6ba7dcSVincent Cheng 
13417ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel,
13427ea5fda2SMin Li 			       bool enable,
13437ea5fda2SMin Li 			       struct ptp_perout_request *perout)
13447ea5fda2SMin Li {
1345e8b4d8b5SVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13467ea5fda2SMin Li 	unsigned int flags = perout->flags;
1347e8b4d8b5SVincent Cheng 	struct timespec64 ts = {0, 0};
1348e8b4d8b5SVincent Cheng 	int err;
13497ea5fda2SMin Li 
13507ea5fda2SMin Li 	if (flags == PEROUT_ENABLE_OUTPUT_MASK)
1351e8b4d8b5SVincent Cheng 		err = idtcm_output_mask_enable(channel, enable);
1352e8b4d8b5SVincent Cheng 	else
1353e8b4d8b5SVincent Cheng 		err = idtcm_output_enable(channel, enable, perout->index);
13547ea5fda2SMin Li 
1355e8b4d8b5SVincent Cheng 	if (err) {
1356e8b4d8b5SVincent Cheng 		dev_err(&idtcm->client->dev, "Unable to set output enable");
1357e8b4d8b5SVincent Cheng 		return err;
1358e8b4d8b5SVincent Cheng 	}
1359e8b4d8b5SVincent Cheng 
1360e8b4d8b5SVincent Cheng 	/* Align output to internal 1 PPS */
1361e8b4d8b5SVincent Cheng 	return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS);
13627ea5fda2SMin Li }
13637ea5fda2SMin Li 
13647260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel,
13657260d1c8SMin Li 			      enum pll_mode *pll_mode)
13667260d1c8SMin Li {
13677260d1c8SMin Li 	struct idtcm *idtcm = channel->idtcm;
13687260d1c8SMin Li 	int err;
13697260d1c8SMin Li 	u8 dpll_mode;
13707260d1c8SMin Li 
13717260d1c8SMin Li 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13727260d1c8SMin Li 			 &dpll_mode, sizeof(dpll_mode));
13737260d1c8SMin Li 	if (err)
13747260d1c8SMin Li 		return err;
13757260d1c8SMin Li 
13767260d1c8SMin Li 	*pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
13777260d1c8SMin Li 
13787260d1c8SMin Li 	return 0;
13797260d1c8SMin Li }
13807260d1c8SMin Li 
13813a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel,
13823a6ba7dcSVincent Cheng 			      enum pll_mode pll_mode)
13833a6ba7dcSVincent Cheng {
13843a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13853a6ba7dcSVincent Cheng 	int err;
13863a6ba7dcSVincent Cheng 	u8 dpll_mode;
13873a6ba7dcSVincent Cheng 
13883a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13893a6ba7dcSVincent Cheng 			 &dpll_mode, sizeof(dpll_mode));
13903a6ba7dcSVincent Cheng 	if (err)
13913a6ba7dcSVincent Cheng 		return err;
13923a6ba7dcSVincent Cheng 
13933a6ba7dcSVincent Cheng 	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
13943a6ba7dcSVincent Cheng 
13953a6ba7dcSVincent Cheng 	dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
13963a6ba7dcSVincent Cheng 
13973a6ba7dcSVincent Cheng 	channel->pll_mode = pll_mode;
13983a6ba7dcSVincent Cheng 
13993a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
14003a6ba7dcSVincent Cheng 			  &dpll_mode, sizeof(dpll_mode));
14013a6ba7dcSVincent Cheng 	if (err)
14023a6ba7dcSVincent Cheng 		return err;
14033a6ba7dcSVincent Cheng 
14043a6ba7dcSVincent Cheng 	return 0;
14053a6ba7dcSVincent Cheng }
14063a6ba7dcSVincent Cheng 
14073a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */
14083a6ba7dcSVincent Cheng 
1409425d2b1cSVincent Cheng /**
1410425d2b1cSVincent Cheng  * @brief Maximum absolute value for write phase offset in picoseconds
1411425d2b1cSVincent Cheng  *
1412425d2b1cSVincent Cheng  * Destination signed register is 32-bit register in resolution of 50ps
1413425d2b1cSVincent Cheng  *
1414425d2b1cSVincent Cheng  * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1415425d2b1cSVincent Cheng  */
1416425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1417425d2b1cSVincent Cheng {
1418425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1419425d2b1cSVincent Cheng 	int err;
1420425d2b1cSVincent Cheng 	u8 i;
1421425d2b1cSVincent Cheng 	u8 buf[4] = {0};
1422425d2b1cSVincent Cheng 	s32 phase_50ps;
1423425d2b1cSVincent Cheng 	s64 offset_ps;
1424425d2b1cSVincent Cheng 
1425425d2b1cSVincent Cheng 	if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
1426425d2b1cSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
1427425d2b1cSVincent Cheng 		if (err)
1428425d2b1cSVincent Cheng 			return err;
1429425d2b1cSVincent Cheng 	}
1430425d2b1cSVincent Cheng 
1431425d2b1cSVincent Cheng 	offset_ps = (s64)delta_ns * 1000;
1432425d2b1cSVincent Cheng 
1433425d2b1cSVincent Cheng 	/*
1434425d2b1cSVincent Cheng 	 * Check for 32-bit signed max * 50:
1435425d2b1cSVincent Cheng 	 *
1436425d2b1cSVincent Cheng 	 * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1437425d2b1cSVincent Cheng 	 */
1438425d2b1cSVincent Cheng 	if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
1439425d2b1cSVincent Cheng 		offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
1440425d2b1cSVincent Cheng 	else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
1441425d2b1cSVincent Cheng 		offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
1442425d2b1cSVincent Cheng 
14437260d1c8SMin Li 	phase_50ps = div_s64(offset_ps, 50);
1444425d2b1cSVincent Cheng 
1445425d2b1cSVincent Cheng 	for (i = 0; i < 4; i++) {
1446425d2b1cSVincent Cheng 		buf[i] = phase_50ps & 0xff;
1447425d2b1cSVincent Cheng 		phase_50ps >>= 8;
1448425d2b1cSVincent Cheng 	}
1449425d2b1cSVincent Cheng 
1450425d2b1cSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1451425d2b1cSVincent Cheng 			  buf, sizeof(buf));
1452425d2b1cSVincent Cheng 
1453425d2b1cSVincent Cheng 	return err;
1454425d2b1cSVincent Cheng }
1455425d2b1cSVincent Cheng 
14567ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
14573a6ba7dcSVincent Cheng {
14583a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
14593a6ba7dcSVincent Cheng 	u8 i;
14603a6ba7dcSVincent Cheng 	int err;
14613a6ba7dcSVincent Cheng 	u8 buf[6] = {0};
14623a6ba7dcSVincent Cheng 	s64 fcw;
14633a6ba7dcSVincent Cheng 
14643a6ba7dcSVincent Cheng 	if (channel->pll_mode  != PLL_MODE_WRITE_FREQUENCY) {
14653a6ba7dcSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
14663a6ba7dcSVincent Cheng 		if (err)
14673a6ba7dcSVincent Cheng 			return err;
14683a6ba7dcSVincent Cheng 	}
14693a6ba7dcSVincent Cheng 
14703a6ba7dcSVincent Cheng 	/*
14713a6ba7dcSVincent Cheng 	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
14723a6ba7dcSVincent Cheng 	 *
14733a6ba7dcSVincent Cheng 	 * adjfreq:
14743a6ba7dcSVincent Cheng 	 *       ppb * 10^9
14753a6ba7dcSVincent Cheng 	 * FCW = ----------
14763a6ba7dcSVincent Cheng 	 *          111
14773a6ba7dcSVincent Cheng 	 *
14783a6ba7dcSVincent Cheng 	 * adjfine:
14793a6ba7dcSVincent Cheng 	 *       ppm_16 * 5^12
14803a6ba7dcSVincent Cheng 	 * FCW = -------------
14813a6ba7dcSVincent Cheng 	 *         111 * 2^4
14823a6ba7dcSVincent Cheng 	 */
14833a6ba7dcSVincent Cheng 
14843a6ba7dcSVincent Cheng 	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
14857ea5fda2SMin Li 	fcw = scaled_ppm * 244140625ULL;
14863a6ba7dcSVincent Cheng 
14877260d1c8SMin Li 	fcw = div_s64(fcw, 1776);
14883a6ba7dcSVincent Cheng 
14893a6ba7dcSVincent Cheng 	for (i = 0; i < 6; i++) {
14903a6ba7dcSVincent Cheng 		buf[i] = fcw & 0xff;
14913a6ba7dcSVincent Cheng 		fcw >>= 8;
14923a6ba7dcSVincent Cheng 	}
14933a6ba7dcSVincent Cheng 
14943a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
14953a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
14963a6ba7dcSVincent Cheng 
14973a6ba7dcSVincent Cheng 	return err;
14983a6ba7dcSVincent Cheng }
14993a6ba7dcSVincent Cheng 
15003a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
15013a6ba7dcSVincent Cheng {
1502fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15033a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15043a6ba7dcSVincent Cheng 	int err;
15053a6ba7dcSVincent Cheng 
15063a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
15073a6ba7dcSVincent Cheng 
15083a6ba7dcSVincent Cheng 	err = _idtcm_gettime(channel, ts);
15097ea5fda2SMin Li 	if (err)
15101c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "Failed at line %d in %s!",
15111c49d3e9SVincent Cheng 			__LINE__, __func__);
15127ea5fda2SMin Li 
15133a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
15143a6ba7dcSVincent Cheng 
15153a6ba7dcSVincent Cheng 	return err;
15163a6ba7dcSVincent Cheng }
15173a6ba7dcSVincent Cheng 
1518da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
15193a6ba7dcSVincent Cheng 				    const struct timespec64 *ts)
15203a6ba7dcSVincent Cheng {
1521fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15223a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15233a6ba7dcSVincent Cheng 	int err;
15243a6ba7dcSVincent Cheng 
15253a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
15263a6ba7dcSVincent Cheng 
1527da948233SMin Li 	err = _idtcm_settime_deprecated(channel, ts);
15287ea5fda2SMin Li 	if (err)
15297ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15301c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
15317ea5fda2SMin Li 
15327ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
15337ea5fda2SMin Li 
15347ea5fda2SMin Li 	return err;
15357ea5fda2SMin Li }
15367ea5fda2SMin Li 
1537da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp,
15387ea5fda2SMin Li 			 const struct timespec64 *ts)
15397ea5fda2SMin Li {
1540fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15417ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
15427ea5fda2SMin Li 	int err;
15437ea5fda2SMin Li 
15447ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
15457ea5fda2SMin Li 
1546da948233SMin Li 	err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
1547da948233SMin Li 	if (err)
1548da948233SMin Li 		dev_err(&idtcm->client->dev,
15491c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
1550da948233SMin Li 
1551da948233SMin Li 	mutex_unlock(&idtcm->reg_lock);
1552da948233SMin Li 
1553da948233SMin Li 	return err;
1554da948233SMin Li }
1555da948233SMin Li 
1556da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
1557da948233SMin Li {
1558fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1559da948233SMin Li 	struct idtcm *idtcm = channel->idtcm;
1560da948233SMin Li 	int err;
1561da948233SMin Li 
1562da948233SMin Li 	mutex_lock(&idtcm->reg_lock);
1563da948233SMin Li 
1564da948233SMin Li 	err = _idtcm_adjtime_deprecated(channel, delta);
15657ea5fda2SMin Li 	if (err)
15667ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15671c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
15687ea5fda2SMin Li 
15693a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
15703a6ba7dcSVincent Cheng 
15713a6ba7dcSVincent Cheng 	return err;
15723a6ba7dcSVincent Cheng }
15733a6ba7dcSVincent Cheng 
15743a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
15753a6ba7dcSVincent Cheng {
1576fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15773a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15787ea5fda2SMin Li 	struct timespec64 ts;
15797ea5fda2SMin Li 	enum scsr_tod_write_type_sel type;
15807ea5fda2SMin Li 	int err;
15817ea5fda2SMin Li 
1582da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
15837ea5fda2SMin Li 		err = idtcm_do_phase_pull_in(channel, delta, 0);
15847ea5fda2SMin Li 		if (err)
15857ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
15861c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
15877ea5fda2SMin Li 		return err;
15887ea5fda2SMin Li 	}
15897ea5fda2SMin Li 
15907ea5fda2SMin Li 	if (delta >= 0) {
15917ea5fda2SMin Li 		ts = ns_to_timespec64(delta);
15927ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
15937ea5fda2SMin Li 	} else {
15947ea5fda2SMin Li 		ts = ns_to_timespec64(-delta);
15957ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
15967ea5fda2SMin Li 	}
15977ea5fda2SMin Li 
15987ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
15997ea5fda2SMin Li 
1600da948233SMin Li 	err = _idtcm_settime(channel, &ts, type);
16017ea5fda2SMin Li 	if (err)
16027ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16031c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16047ea5fda2SMin Li 
16053a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
16063a6ba7dcSVincent Cheng 
16073a6ba7dcSVincent Cheng 	return err;
16083a6ba7dcSVincent Cheng }
16093a6ba7dcSVincent Cheng 
1610425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1611425d2b1cSVincent Cheng {
1612fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1613425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1614425d2b1cSVincent Cheng 	int err;
1615425d2b1cSVincent Cheng 
1616425d2b1cSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
1617425d2b1cSVincent Cheng 
1618425d2b1cSVincent Cheng 	err = _idtcm_adjphase(channel, delta);
16197ea5fda2SMin Li 	if (err)
16207ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16211c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16227ea5fda2SMin Li 
16237ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
16247ea5fda2SMin Li 
16257ea5fda2SMin Li 	return err;
16267ea5fda2SMin Li }
16277ea5fda2SMin Li 
16287ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
16297ea5fda2SMin Li {
1630fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
16317ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
16327ea5fda2SMin Li 	int err;
16337ea5fda2SMin Li 
16347ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
16357ea5fda2SMin Li 
16367ea5fda2SMin Li 	err = _idtcm_adjfine(channel, scaled_ppm);
16377ea5fda2SMin Li 	if (err)
16387ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16391c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16407ea5fda2SMin Li 
1641425d2b1cSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
1642425d2b1cSVincent Cheng 
1643425d2b1cSVincent Cheng 	return err;
1644425d2b1cSVincent Cheng }
1645425d2b1cSVincent Cheng 
16463a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp,
16473a6ba7dcSVincent Cheng 			struct ptp_clock_request *rq, int on)
16483a6ba7dcSVincent Cheng {
16497ea5fda2SMin Li 	int err;
1650fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
16513a6ba7dcSVincent Cheng 
16523a6ba7dcSVincent Cheng 	switch (rq->type) {
16533a6ba7dcSVincent Cheng 	case PTP_CLK_REQ_PEROUT:
16547ea5fda2SMin Li 		if (!on) {
16557ea5fda2SMin Li 			err = idtcm_perout_enable(channel, false, &rq->perout);
16567ea5fda2SMin Li 			if (err)
16577ea5fda2SMin Li 				dev_err(&channel->idtcm->client->dev,
16581c49d3e9SVincent Cheng 					"Failed at line %d in %s!",
16591c49d3e9SVincent Cheng 					__LINE__, __func__);
16607ea5fda2SMin Li 			return err;
16617ea5fda2SMin Li 		}
16623a6ba7dcSVincent Cheng 
16633a6ba7dcSVincent Cheng 		/* Only accept a 1-PPS aligned to the second. */
16643a6ba7dcSVincent Cheng 		if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
16653a6ba7dcSVincent Cheng 		    rq->perout.period.nsec)
16663a6ba7dcSVincent Cheng 			return -ERANGE;
16673a6ba7dcSVincent Cheng 
16687ea5fda2SMin Li 		err = idtcm_perout_enable(channel, true, &rq->perout);
16697ea5fda2SMin Li 		if (err)
16707ea5fda2SMin Li 			dev_err(&channel->idtcm->client->dev,
16711c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
16727ea5fda2SMin Li 		return err;
16733a6ba7dcSVincent Cheng 	default:
16743a6ba7dcSVincent Cheng 		break;
16753a6ba7dcSVincent Cheng 	}
16763a6ba7dcSVincent Cheng 
16773a6ba7dcSVincent Cheng 	return -EOPNOTSUPP;
16783a6ba7dcSVincent Cheng }
16793a6ba7dcSVincent Cheng 
16807ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm,
16817ea5fda2SMin Li 				u8 pll,
16827ea5fda2SMin Li 				u8 sync_src,
16837ea5fda2SMin Li 				u8 qn,
16847ea5fda2SMin Li 				u8 qn_plus_1)
16857ea5fda2SMin Li {
16867ea5fda2SMin Li 	int err;
16877ea5fda2SMin Li 	u8 val;
16887ea5fda2SMin Li 	u16 dpll;
16897ea5fda2SMin Li 	u16 out0 = 0, out1 = 0;
16907ea5fda2SMin Li 
16917ea5fda2SMin Li 	if ((qn == 0) && (qn_plus_1 == 0))
16927ea5fda2SMin Li 		return 0;
16937ea5fda2SMin Li 
16947ea5fda2SMin Li 	switch (pll) {
16957ea5fda2SMin Li 	case 0:
16967ea5fda2SMin Li 		dpll = DPLL_0;
16977ea5fda2SMin Li 		if (qn)
16987ea5fda2SMin Li 			out0 = OUTPUT_0;
16997ea5fda2SMin Li 		if (qn_plus_1)
17007ea5fda2SMin Li 			out1 = OUTPUT_1;
17017ea5fda2SMin Li 		break;
17027ea5fda2SMin Li 	case 1:
17037ea5fda2SMin Li 		dpll = DPLL_1;
17047ea5fda2SMin Li 		if (qn)
17057ea5fda2SMin Li 			out0 = OUTPUT_2;
17067ea5fda2SMin Li 		if (qn_plus_1)
17077ea5fda2SMin Li 			out1 = OUTPUT_3;
17087ea5fda2SMin Li 		break;
17097ea5fda2SMin Li 	case 2:
17107ea5fda2SMin Li 		dpll = DPLL_2;
17117ea5fda2SMin Li 		if (qn)
17127ea5fda2SMin Li 			out0 = OUTPUT_4;
17137ea5fda2SMin Li 		if (qn_plus_1)
17147ea5fda2SMin Li 			out1 = OUTPUT_5;
17157ea5fda2SMin Li 		break;
17167ea5fda2SMin Li 	case 3:
17177ea5fda2SMin Li 		dpll = DPLL_3;
17187ea5fda2SMin Li 		if (qn)
17197ea5fda2SMin Li 			out0 = OUTPUT_6;
17207ea5fda2SMin Li 		if (qn_plus_1)
17217ea5fda2SMin Li 			out1 = OUTPUT_7;
17227ea5fda2SMin Li 		break;
17237ea5fda2SMin Li 	case 4:
17247ea5fda2SMin Li 		dpll = DPLL_4;
17257ea5fda2SMin Li 		if (qn)
17267ea5fda2SMin Li 			out0 = OUTPUT_8;
17277ea5fda2SMin Li 		break;
17287ea5fda2SMin Li 	case 5:
17297ea5fda2SMin Li 		dpll = DPLL_5;
17307ea5fda2SMin Li 		if (qn)
17317ea5fda2SMin Li 			out0 = OUTPUT_9;
17327ea5fda2SMin Li 		if (qn_plus_1)
17337ea5fda2SMin Li 			out1 = OUTPUT_8;
17347ea5fda2SMin Li 		break;
17357ea5fda2SMin Li 	case 6:
17367ea5fda2SMin Li 		dpll = DPLL_6;
17377ea5fda2SMin Li 		if (qn)
17387ea5fda2SMin Li 			out0 = OUTPUT_10;
17397ea5fda2SMin Li 		if (qn_plus_1)
17407ea5fda2SMin Li 			out1 = OUTPUT_11;
17417ea5fda2SMin Li 		break;
17427ea5fda2SMin Li 	case 7:
17437ea5fda2SMin Li 		dpll = DPLL_7;
17447ea5fda2SMin Li 		if (qn)
17457ea5fda2SMin Li 			out0 = OUTPUT_11;
17467ea5fda2SMin Li 		break;
17477ea5fda2SMin Li 	default:
17487ea5fda2SMin Li 		return -EINVAL;
17497ea5fda2SMin Li 	}
17507ea5fda2SMin Li 
17517ea5fda2SMin Li 	/*
17527ea5fda2SMin Li 	 * Enable OUTPUT OUT_SYNC.
17537ea5fda2SMin Li 	 */
17547ea5fda2SMin Li 	if (out0) {
17557ea5fda2SMin Li 		err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17567ea5fda2SMin Li 		if (err)
17577ea5fda2SMin Li 			return err;
17587ea5fda2SMin Li 
17597ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17607ea5fda2SMin Li 
17617ea5fda2SMin Li 		err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17627ea5fda2SMin Li 		if (err)
17637ea5fda2SMin Li 			return err;
17647ea5fda2SMin Li 	}
17657ea5fda2SMin Li 
17667ea5fda2SMin Li 	if (out1) {
17677ea5fda2SMin Li 		err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17687ea5fda2SMin Li 		if (err)
17697ea5fda2SMin Li 			return err;
17707ea5fda2SMin Li 
17717ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17727ea5fda2SMin Li 
17737ea5fda2SMin Li 		err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17747ea5fda2SMin Li 		if (err)
17757ea5fda2SMin Li 			return err;
17767ea5fda2SMin Li 	}
17777ea5fda2SMin Li 
17787ea5fda2SMin Li 	/* enable dpll sync tod pps, must be set before dpll_mode */
17797ea5fda2SMin Li 	err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
17807ea5fda2SMin Li 	if (err)
17817ea5fda2SMin Li 		return err;
17827ea5fda2SMin Li 
17837ea5fda2SMin Li 	val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
17847ea5fda2SMin Li 	val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
17857ea5fda2SMin Li 	val |= TOD_SYNC_EN;
17867ea5fda2SMin Li 
17877ea5fda2SMin Li 	return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
17887ea5fda2SMin Li }
17897ea5fda2SMin Li 
17907ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel)
17917ea5fda2SMin Li {
17927ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
17937ea5fda2SMin Li 	u8 pll;
17947ea5fda2SMin Li 	u8 sync_src;
17957ea5fda2SMin Li 	u8 qn;
17967ea5fda2SMin Li 	u8 qn_plus_1;
17977ea5fda2SMin Li 	u8 cfg;
17987ea5fda2SMin Li 	int err = 0;
17997ea5fda2SMin Li 	u16 output_mask = channel->output_mask;
18007ea5fda2SMin Li 	u8 out8_mux = 0;
18017ea5fda2SMin Li 	u8 out11_mux = 0;
18027ea5fda2SMin Li 	u8 temp;
18037ea5fda2SMin Li 
18047ea5fda2SMin Li 	/*
18057ea5fda2SMin Li 	 * set tod_out_sync_enable to 0.
18067ea5fda2SMin Li 	 */
18077ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18087ea5fda2SMin Li 	if (err)
18097ea5fda2SMin Li 		return err;
18107ea5fda2SMin Li 
18117ea5fda2SMin Li 	cfg &= ~TOD_OUT_SYNC_ENABLE;
18127ea5fda2SMin Li 
18137ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18147ea5fda2SMin Li 	if (err)
18157ea5fda2SMin Li 		return err;
18167ea5fda2SMin Li 
18177ea5fda2SMin Li 	switch (channel->tod_n) {
18187ea5fda2SMin Li 	case TOD_0:
18197ea5fda2SMin Li 		sync_src = 0;
18207ea5fda2SMin Li 		break;
18217ea5fda2SMin Li 	case TOD_1:
18227ea5fda2SMin Li 		sync_src = 1;
18237ea5fda2SMin Li 		break;
18247ea5fda2SMin Li 	case TOD_2:
18257ea5fda2SMin Li 		sync_src = 2;
18267ea5fda2SMin Li 		break;
18277ea5fda2SMin Li 	case TOD_3:
18287ea5fda2SMin Li 		sync_src = 3;
18297ea5fda2SMin Li 		break;
18307ea5fda2SMin Li 	default:
18317ea5fda2SMin Li 		return -EINVAL;
18327ea5fda2SMin Li 	}
18337ea5fda2SMin Li 
1834fcfd3757SVincent Cheng 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp));
18357ea5fda2SMin Li 	if (err)
18367ea5fda2SMin Li 		return err;
18377ea5fda2SMin Li 
18387ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18397ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18407ea5fda2SMin Li 		out8_mux = 1;
18417ea5fda2SMin Li 
1842fcfd3757SVincent Cheng 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp));
18437ea5fda2SMin Li 	if (err)
18447ea5fda2SMin Li 		return err;
18457ea5fda2SMin Li 
18467ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18477ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18487ea5fda2SMin Li 		out11_mux = 1;
18497ea5fda2SMin Li 
18507ea5fda2SMin Li 	for (pll = 0; pll < 8; pll++) {
18517ea5fda2SMin Li 		qn = 0;
18527ea5fda2SMin Li 		qn_plus_1 = 0;
18537ea5fda2SMin Li 
18547ea5fda2SMin Li 		if (pll < 4) {
18557ea5fda2SMin Li 			/* First 4 pll has 2 outputs */
18567ea5fda2SMin Li 			qn = output_mask & 0x1;
18577ea5fda2SMin Li 			output_mask = output_mask >> 1;
18587ea5fda2SMin Li 			qn_plus_1 = output_mask & 0x1;
18597ea5fda2SMin Li 			output_mask = output_mask >> 1;
18607ea5fda2SMin Li 		} else if (pll == 4) {
18617ea5fda2SMin Li 			if (out8_mux == 0) {
18627ea5fda2SMin Li 				qn = output_mask & 0x1;
18637ea5fda2SMin Li 				output_mask = output_mask >> 1;
18647ea5fda2SMin Li 			}
18657ea5fda2SMin Li 		} else if (pll == 5) {
18667ea5fda2SMin Li 			if (out8_mux) {
18677ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
18687ea5fda2SMin Li 				output_mask = output_mask >> 1;
18697ea5fda2SMin Li 			}
18707ea5fda2SMin Li 			qn = output_mask & 0x1;
18717ea5fda2SMin Li 			output_mask = output_mask >> 1;
18727ea5fda2SMin Li 		} else if (pll == 6) {
18737ea5fda2SMin Li 			qn = output_mask & 0x1;
18747ea5fda2SMin Li 			output_mask = output_mask >> 1;
18757ea5fda2SMin Li 			if (out11_mux) {
18767ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
18777ea5fda2SMin Li 				output_mask = output_mask >> 1;
18787ea5fda2SMin Li 			}
18797ea5fda2SMin Li 		} else if (pll == 7) {
18807ea5fda2SMin Li 			if (out11_mux == 0) {
18817ea5fda2SMin Li 				qn = output_mask & 0x1;
18827ea5fda2SMin Li 				output_mask = output_mask >> 1;
18837ea5fda2SMin Li 			}
18847ea5fda2SMin Li 		}
18857ea5fda2SMin Li 
18867ea5fda2SMin Li 		if ((qn != 0) || (qn_plus_1 != 0))
18877ea5fda2SMin Li 			err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
18887ea5fda2SMin Li 					       qn_plus_1);
18897ea5fda2SMin Li 		if (err)
18907ea5fda2SMin Li 			return err;
18917ea5fda2SMin Li 	}
18927ea5fda2SMin Li 
18937ea5fda2SMin Li 	return err;
18947ea5fda2SMin Li }
18957ea5fda2SMin Li 
18963a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel)
18973a6ba7dcSVincent Cheng {
18983a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
18993a6ba7dcSVincent Cheng 	struct timespec64 ts = {0, 0};
19003a6ba7dcSVincent Cheng 	u8 cfg;
19013a6ba7dcSVincent Cheng 	int err;
19023a6ba7dcSVincent Cheng 
19033a6ba7dcSVincent Cheng 	/*
19043a6ba7dcSVincent Cheng 	 * Start the TOD clock ticking.
19053a6ba7dcSVincent Cheng 	 */
19063a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19073a6ba7dcSVincent Cheng 	if (err)
19083a6ba7dcSVincent Cheng 		return err;
19093a6ba7dcSVincent Cheng 
19103a6ba7dcSVincent Cheng 	cfg |= TOD_ENABLE;
19113a6ba7dcSVincent Cheng 
19123a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19133a6ba7dcSVincent Cheng 	if (err)
19143a6ba7dcSVincent Cheng 		return err;
19153a6ba7dcSVincent Cheng 
1916da948233SMin Li 	if (idtcm->deprecated)
1917da948233SMin Li 		return _idtcm_settime_deprecated(channel, &ts);
1918da948233SMin Li 	else
1919da948233SMin Li 		return _idtcm_settime(channel, &ts,
1920da948233SMin Li 				      SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
19213a6ba7dcSVincent Cheng }
19223a6ba7dcSVincent Cheng 
1923da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm)
19243a6ba7dcSVincent Cheng {
19253a6ba7dcSVincent Cheng 	u8 major;
19263a6ba7dcSVincent Cheng 	u8 minor;
19273a6ba7dcSVincent Cheng 	u8 hotfix;
19283a6ba7dcSVincent Cheng 	u16 product_id;
19293a6ba7dcSVincent Cheng 	u8 hw_rev_id;
19301ece2fbeSVincent Cheng 	u8 config_select;
19313a6ba7dcSVincent Cheng 
19323a6ba7dcSVincent Cheng 	idtcm_read_major_release(idtcm, &major);
19333a6ba7dcSVincent Cheng 	idtcm_read_minor_release(idtcm, &minor);
19343a6ba7dcSVincent Cheng 	idtcm_read_hotfix_release(idtcm, &hotfix);
19353a6ba7dcSVincent Cheng 
19363a6ba7dcSVincent Cheng 	idtcm_read_product_id(idtcm, &product_id);
19373a6ba7dcSVincent Cheng 	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
19383a6ba7dcSVincent Cheng 
19391ece2fbeSVincent Cheng 	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
19401ece2fbeSVincent Cheng 
19417ea5fda2SMin Li 	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
19427ea5fda2SMin Li 		 major, minor, hotfix);
19437ea5fda2SMin Li 
1944da948233SMin Li 	if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
1945da948233SMin Li 		idtcm->deprecated = 0;
1946da948233SMin Li 	else
1947da948233SMin Li 		idtcm->deprecated = 1;
1948da948233SMin Li 
19491c49d3e9SVincent Cheng 	dev_info(&idtcm->client->dev,
19501c49d3e9SVincent Cheng 		 "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d",
19511c49d3e9SVincent Cheng 		 major, minor, hotfix,
19521ece2fbeSVincent Cheng 		 product_id, hw_rev_id, config_select);
19533a6ba7dcSVincent Cheng }
19543a6ba7dcSVincent Cheng 
19556485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = {
19563a6ba7dcSVincent Cheng 	.owner		= THIS_MODULE,
19573a6ba7dcSVincent Cheng 	.max_adj	= 244000,
19587ea5fda2SMin Li 	.n_per_out	= 12,
1959425d2b1cSVincent Cheng 	.adjphase	= &idtcm_adjphase,
19607ea5fda2SMin Li 	.adjfine	= &idtcm_adjfine,
19613a6ba7dcSVincent Cheng 	.adjtime	= &idtcm_adjtime,
19623a6ba7dcSVincent Cheng 	.gettime64	= &idtcm_gettime,
19633a6ba7dcSVincent Cheng 	.settime64	= &idtcm_settime,
19643a6ba7dcSVincent Cheng 	.enable		= &idtcm_enable,
19653a6ba7dcSVincent Cheng };
19663a6ba7dcSVincent Cheng 
1967da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = {
1968da948233SMin Li 	.owner		= THIS_MODULE,
1969da948233SMin Li 	.max_adj	= 244000,
1970da948233SMin Li 	.n_per_out	= 12,
1971da948233SMin Li 	.adjphase	= &idtcm_adjphase,
1972da948233SMin Li 	.adjfine	= &idtcm_adjfine,
1973da948233SMin Li 	.adjtime	= &idtcm_adjtime_deprecated,
1974da948233SMin Li 	.gettime64	= &idtcm_gettime,
1975da948233SMin Li 	.settime64	= &idtcm_settime_deprecated,
1976da948233SMin Li 	.enable		= &idtcm_enable,
1977da948233SMin Li };
1978da948233SMin Li 
19797ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel)
19803a6ba7dcSVincent Cheng {
19817ea5fda2SMin Li 	int err = 0;
19823a6ba7dcSVincent Cheng 
19837ea5fda2SMin Li 	switch (channel->pll) {
19843a6ba7dcSVincent Cheng 	case 0:
19853a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_0;
19863a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_0;
19873a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_0;
19883a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_0;
19893a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_0;
19903a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
19913a6ba7dcSVincent Cheng 		break;
19923a6ba7dcSVincent Cheng 	case 1:
19933a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_1;
19943a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_1;
19953a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_1;
19963a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_1;
19973a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_1;
19983a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
19993a6ba7dcSVincent Cheng 		break;
20003a6ba7dcSVincent Cheng 	case 2:
20013a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_2;
20023a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_2;
20033a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_2;
20043a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_2;
20053a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_2;
20063a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
20073a6ba7dcSVincent Cheng 		break;
20083a6ba7dcSVincent Cheng 	case 3:
20093a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_3;
20103a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_3;
20113a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_3;
20123a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_3;
20133a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_3;
20143a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
20153a6ba7dcSVincent Cheng 		break;
20167ea5fda2SMin Li 	case 4:
20177ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_4;
20187ea5fda2SMin Li 		channel->dpll_n = DPLL_4;
20197ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_4;
20207ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_4;
20217ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_4;
20227ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
20237ea5fda2SMin Li 		break;
20247ea5fda2SMin Li 	case 5:
20257ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_5;
20267ea5fda2SMin Li 		channel->dpll_n = DPLL_5;
20277ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_5;
20287ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_5;
20297ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_5;
20307ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
20317ea5fda2SMin Li 		break;
20327ea5fda2SMin Li 	case 6:
20337ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_6;
20347ea5fda2SMin Li 		channel->dpll_n = DPLL_6;
20357ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_6;
20367ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_6;
20377ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_6;
20387ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
20397ea5fda2SMin Li 		break;
20407ea5fda2SMin Li 	case 7:
20417ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_7;
20427ea5fda2SMin Li 		channel->dpll_n = DPLL_7;
20437ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_7;
20447ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_7;
20457ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_7;
20467ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
20477ea5fda2SMin Li 		break;
20487ea5fda2SMin Li 	default:
20497ea5fda2SMin Li 		err = -EINVAL;
20507ea5fda2SMin Li 	}
20517ea5fda2SMin Li 
20527ea5fda2SMin Li 	return err;
20537ea5fda2SMin Li }
20547ea5fda2SMin Li 
20557ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
20567ea5fda2SMin Li {
20577ea5fda2SMin Li 	struct idtcm_channel *channel;
20587ea5fda2SMin Li 	int err;
20597ea5fda2SMin Li 
20607ea5fda2SMin Li 	if (!(index < MAX_TOD))
20617ea5fda2SMin Li 		return -EINVAL;
20627ea5fda2SMin Li 
20637ea5fda2SMin Li 	channel = &idtcm->channel[index];
20647ea5fda2SMin Li 
20657ea5fda2SMin Li 	/* Set pll addresses */
20667ea5fda2SMin Li 	err = configure_channel_pll(channel);
20677ea5fda2SMin Li 	if (err)
20687ea5fda2SMin Li 		return err;
20697ea5fda2SMin Li 
20707ea5fda2SMin Li 	/* Set tod addresses */
20717ea5fda2SMin Li 	switch (index) {
20727ea5fda2SMin Li 	case 0:
20737ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_0;
20747ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_0;
20757ea5fda2SMin Li 		channel->tod_n = TOD_0;
20767ea5fda2SMin Li 		break;
20777ea5fda2SMin Li 	case 1:
20787ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_1;
20797ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_1;
20807ea5fda2SMin Li 		channel->tod_n = TOD_1;
20817ea5fda2SMin Li 		break;
20827ea5fda2SMin Li 	case 2:
20837ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_2;
20847ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_2;
20857ea5fda2SMin Li 		channel->tod_n = TOD_2;
20867ea5fda2SMin Li 		break;
20877ea5fda2SMin Li 	case 3:
20887ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_3;
20897ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_3;
20907ea5fda2SMin Li 		channel->tod_n = TOD_3;
20917ea5fda2SMin Li 		break;
20923a6ba7dcSVincent Cheng 	default:
20933a6ba7dcSVincent Cheng 		return -EINVAL;
20943a6ba7dcSVincent Cheng 	}
20953a6ba7dcSVincent Cheng 
20963a6ba7dcSVincent Cheng 	channel->idtcm = idtcm;
20973a6ba7dcSVincent Cheng 
2098da948233SMin Li 	if (idtcm->deprecated)
2099da948233SMin Li 		channel->caps = idtcm_caps_deprecated;
21007ea5fda2SMin Li 	else
21013a6ba7dcSVincent Cheng 		channel->caps = idtcm_caps;
21027ea5fda2SMin Li 
21033a6ba7dcSVincent Cheng 	snprintf(channel->caps.name, sizeof(channel->caps.name),
21047ea5fda2SMin Li 		 "IDT CM TOD%u", index);
21057ea5fda2SMin Li 
2106da948233SMin Li 	if (!idtcm->deprecated) {
21077ea5fda2SMin Li 		err = idtcm_enable_tod_sync(channel);
21087ea5fda2SMin Li 		if (err) {
21097ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
21101c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
21117ea5fda2SMin Li 			return err;
21127ea5fda2SMin Li 		}
21137ea5fda2SMin Li 	}
21143a6ba7dcSVincent Cheng 
21157260d1c8SMin Li 	/* Sync pll mode with hardware */
21167260d1c8SMin Li 	err = idtcm_get_pll_mode(channel, &channel->pll_mode);
21177ea5fda2SMin Li 	if (err) {
21187ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21191c49d3e9SVincent Cheng 			"Error: %s - Unable to read pll mode", __func__);
21203a6ba7dcSVincent Cheng 		return err;
21217ea5fda2SMin Li 	}
21223a6ba7dcSVincent Cheng 
21233a6ba7dcSVincent Cheng 	err = idtcm_enable_tod(channel);
21247ea5fda2SMin Li 	if (err) {
21257ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21261c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
21273a6ba7dcSVincent Cheng 		return err;
21287ea5fda2SMin Li 	}
21293a6ba7dcSVincent Cheng 
21303a6ba7dcSVincent Cheng 	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
21313a6ba7dcSVincent Cheng 
21323a6ba7dcSVincent Cheng 	if (IS_ERR(channel->ptp_clock)) {
21333a6ba7dcSVincent Cheng 		err = PTR_ERR(channel->ptp_clock);
21343a6ba7dcSVincent Cheng 		channel->ptp_clock = NULL;
21353a6ba7dcSVincent Cheng 		return err;
21363a6ba7dcSVincent Cheng 	}
21373a6ba7dcSVincent Cheng 
21383a6ba7dcSVincent Cheng 	if (!channel->ptp_clock)
21393a6ba7dcSVincent Cheng 		return -ENOTSUPP;
21403a6ba7dcSVincent Cheng 
21411c49d3e9SVincent Cheng 	dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d",
21423a6ba7dcSVincent Cheng 		 index, channel->ptp_clock->index);
21433a6ba7dcSVincent Cheng 
21443a6ba7dcSVincent Cheng 	return 0;
21453a6ba7dcSVincent Cheng }
21463a6ba7dcSVincent Cheng 
21473a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm)
21483a6ba7dcSVincent Cheng {
21493a6ba7dcSVincent Cheng 	u8 i;
21503a6ba7dcSVincent Cheng 	struct idtcm_channel *channel;
21513a6ba7dcSVincent Cheng 
21527ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
21533a6ba7dcSVincent Cheng 		channel = &idtcm->channel[i];
21543a6ba7dcSVincent Cheng 
21553a6ba7dcSVincent Cheng 		if (channel->ptp_clock)
21563a6ba7dcSVincent Cheng 			ptp_clock_unregister(channel->ptp_clock);
21573a6ba7dcSVincent Cheng 	}
21583a6ba7dcSVincent Cheng }
21593a6ba7dcSVincent Cheng 
21603a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm)
21613a6ba7dcSVincent Cheng {
21627ea5fda2SMin Li 	idtcm->tod_mask = DEFAULT_TOD_MASK;
21637ea5fda2SMin Li 
21647ea5fda2SMin Li 	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
21657ea5fda2SMin Li 	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
21667ea5fda2SMin Li 	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
21677ea5fda2SMin Li 	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
21683a6ba7dcSVincent Cheng 
21693a6ba7dcSVincent Cheng 	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
21703a6ba7dcSVincent Cheng 	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
21713a6ba7dcSVincent Cheng 	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
21723a6ba7dcSVincent Cheng 	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
21733a6ba7dcSVincent Cheng }
21743a6ba7dcSVincent Cheng 
21753a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client,
21763a6ba7dcSVincent Cheng 		       const struct i2c_device_id *id)
21773a6ba7dcSVincent Cheng {
21783a6ba7dcSVincent Cheng 	struct idtcm *idtcm;
21793a6ba7dcSVincent Cheng 	int err;
21803a6ba7dcSVincent Cheng 	u8 i;
21813a6ba7dcSVincent Cheng 
21823a6ba7dcSVincent Cheng 	/* Unused for now */
21833a6ba7dcSVincent Cheng 	(void)id;
21843a6ba7dcSVincent Cheng 
21853a6ba7dcSVincent Cheng 	idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
21863a6ba7dcSVincent Cheng 
21873a6ba7dcSVincent Cheng 	if (!idtcm)
21883a6ba7dcSVincent Cheng 		return -ENOMEM;
21893a6ba7dcSVincent Cheng 
21903a6ba7dcSVincent Cheng 	idtcm->client = client;
21913a6ba7dcSVincent Cheng 	idtcm->page_offset = 0xff;
21923a6ba7dcSVincent Cheng 	idtcm->calculate_overhead_flag = 0;
21933a6ba7dcSVincent Cheng 
21943a6ba7dcSVincent Cheng 	set_default_masks(idtcm);
21953a6ba7dcSVincent Cheng 
21963a6ba7dcSVincent Cheng 	mutex_init(&idtcm->reg_lock);
21973a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
21983a6ba7dcSVincent Cheng 
2199da948233SMin Li 	idtcm_set_version_info(idtcm);
22003a6ba7dcSVincent Cheng 
22013a6ba7dcSVincent Cheng 	err = idtcm_load_firmware(idtcm, &client->dev);
22023a6ba7dcSVincent Cheng 	if (err)
2203fcfd3757SVincent Cheng 		dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err);
22043a6ba7dcSVincent Cheng 
2205797d3186SVincent Cheng 	wait_for_chip_ready(idtcm);
2206251f4fe2SMin Li 
22077ea5fda2SMin Li 	if (idtcm->tod_mask) {
22087ea5fda2SMin Li 		for (i = 0; i < MAX_TOD; i++) {
22097ea5fda2SMin Li 			if (idtcm->tod_mask & (1 << i)) {
22103a6ba7dcSVincent Cheng 				err = idtcm_enable_channel(idtcm, i);
22117ea5fda2SMin Li 				if (err) {
22127ea5fda2SMin Li 					dev_err(&idtcm->client->dev,
22131c49d3e9SVincent Cheng 						"idtcm_enable_channel %d failed!", i);
22143a6ba7dcSVincent Cheng 					break;
22153a6ba7dcSVincent Cheng 				}
22163a6ba7dcSVincent Cheng 			}
22177ea5fda2SMin Li 		}
22183a6ba7dcSVincent Cheng 	} else {
22193a6ba7dcSVincent Cheng 		dev_err(&idtcm->client->dev,
22201c49d3e9SVincent Cheng 			"no PLLs flagged as PHCs, nothing to do");
22213a6ba7dcSVincent Cheng 		err = -ENODEV;
22223a6ba7dcSVincent Cheng 	}
22233a6ba7dcSVincent Cheng 
22243a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
22253a6ba7dcSVincent Cheng 
22263a6ba7dcSVincent Cheng 	if (err) {
22273a6ba7dcSVincent Cheng 		ptp_clock_unregister_all(idtcm);
22283a6ba7dcSVincent Cheng 		return err;
22293a6ba7dcSVincent Cheng 	}
22303a6ba7dcSVincent Cheng 
22313a6ba7dcSVincent Cheng 	i2c_set_clientdata(client, idtcm);
22323a6ba7dcSVincent Cheng 
22333a6ba7dcSVincent Cheng 	return 0;
22343a6ba7dcSVincent Cheng }
22353a6ba7dcSVincent Cheng 
22363a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client)
22373a6ba7dcSVincent Cheng {
22383a6ba7dcSVincent Cheng 	struct idtcm *idtcm = i2c_get_clientdata(client);
22393a6ba7dcSVincent Cheng 
22403a6ba7dcSVincent Cheng 	ptp_clock_unregister_all(idtcm);
22413a6ba7dcSVincent Cheng 
22423a6ba7dcSVincent Cheng 	mutex_destroy(&idtcm->reg_lock);
22433a6ba7dcSVincent Cheng 
22443a6ba7dcSVincent Cheng 	return 0;
22453a6ba7dcSVincent Cheng }
22463a6ba7dcSVincent Cheng 
22473a6ba7dcSVincent Cheng #ifdef CONFIG_OF
22483a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = {
22493a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34000" },
22503a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34001" },
22513a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34002" },
22523a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34003" },
22533a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34004" },
22543a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34005" },
22553a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34006" },
22563a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34007" },
22573a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34008" },
22583a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34009" },
22593a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34010" },
22603a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34011" },
22613a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34012" },
22623a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34013" },
22633a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34014" },
22643a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34015" },
22653a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34016" },
22663a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34017" },
22673a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34018" },
22683a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34019" },
22693a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34040" },
22703a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34041" },
22713a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34042" },
22723a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34043" },
22733a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34044" },
22743a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34045" },
22753a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34046" },
22763a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34047" },
22773a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34048" },
22783a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34049" },
22793a6ba7dcSVincent Cheng 	{},
22803a6ba7dcSVincent Cheng };
22813a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id);
22823a6ba7dcSVincent Cheng #endif
22833a6ba7dcSVincent Cheng 
22843a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = {
22853a6ba7dcSVincent Cheng 	{ "8a34000" },
22863a6ba7dcSVincent Cheng 	{ "8a34001" },
22873a6ba7dcSVincent Cheng 	{ "8a34002" },
22883a6ba7dcSVincent Cheng 	{ "8a34003" },
22893a6ba7dcSVincent Cheng 	{ "8a34004" },
22903a6ba7dcSVincent Cheng 	{ "8a34005" },
22913a6ba7dcSVincent Cheng 	{ "8a34006" },
22923a6ba7dcSVincent Cheng 	{ "8a34007" },
22933a6ba7dcSVincent Cheng 	{ "8a34008" },
22943a6ba7dcSVincent Cheng 	{ "8a34009" },
22953a6ba7dcSVincent Cheng 	{ "8a34010" },
22963a6ba7dcSVincent Cheng 	{ "8a34011" },
22973a6ba7dcSVincent Cheng 	{ "8a34012" },
22983a6ba7dcSVincent Cheng 	{ "8a34013" },
22993a6ba7dcSVincent Cheng 	{ "8a34014" },
23003a6ba7dcSVincent Cheng 	{ "8a34015" },
23013a6ba7dcSVincent Cheng 	{ "8a34016" },
23023a6ba7dcSVincent Cheng 	{ "8a34017" },
23033a6ba7dcSVincent Cheng 	{ "8a34018" },
23043a6ba7dcSVincent Cheng 	{ "8a34019" },
23053a6ba7dcSVincent Cheng 	{ "8a34040" },
23063a6ba7dcSVincent Cheng 	{ "8a34041" },
23073a6ba7dcSVincent Cheng 	{ "8a34042" },
23083a6ba7dcSVincent Cheng 	{ "8a34043" },
23093a6ba7dcSVincent Cheng 	{ "8a34044" },
23103a6ba7dcSVincent Cheng 	{ "8a34045" },
23113a6ba7dcSVincent Cheng 	{ "8a34046" },
23123a6ba7dcSVincent Cheng 	{ "8a34047" },
23133a6ba7dcSVincent Cheng 	{ "8a34048" },
23143a6ba7dcSVincent Cheng 	{ "8a34049" },
23153a6ba7dcSVincent Cheng 	{},
23163a6ba7dcSVincent Cheng };
23173a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
23183a6ba7dcSVincent Cheng 
23193a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = {
23203a6ba7dcSVincent Cheng 	.driver = {
23213a6ba7dcSVincent Cheng 		.of_match_table	= of_match_ptr(idtcm_dt_id),
23223a6ba7dcSVincent Cheng 		.name		= "idtcm",
23233a6ba7dcSVincent Cheng 	},
23243a6ba7dcSVincent Cheng 	.probe		= idtcm_probe,
23253a6ba7dcSVincent Cheng 	.remove		= idtcm_remove,
23263a6ba7dcSVincent Cheng 	.id_table	= idtcm_i2c_id,
23273a6ba7dcSVincent Cheng };
23283a6ba7dcSVincent Cheng 
23293a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver);
2330