xref: /openbmc/linux/drivers/ptp/ptp_clockmatrix.c (revision fcfd3757)
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 	int err;
286251f4fe2SMin Li 	u8 buf[4] = {0};
287251f4fe2SMin Li 
288251f4fe2SMin Li 	err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
289251f4fe2SMin Li 
290251f4fe2SMin Li 	return err;
291251f4fe2SMin Li }
292251f4fe2SMin Li 
293251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status)
294251f4fe2SMin Li {
295251f4fe2SMin Li 	int err;
296251f4fe2SMin Li 	u8 buf[4] = {0};
297251f4fe2SMin Li 
298251f4fe2SMin Li 	err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
299251f4fe2SMin Li 
300251f4fe2SMin Li 	*status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
301251f4fe2SMin Li 
302251f4fe2SMin Li 	return err;
303251f4fe2SMin Li }
304251f4fe2SMin Li 
305251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm)
306251f4fe2SMin Li {
307251f4fe2SMin Li 	u32 status = 0;
308251f4fe2SMin Li 	u8 i = 30;	/* 30 * 100ms = 3s */
309251f4fe2SMin Li 	int err;
310251f4fe2SMin Li 
311251f4fe2SMin Li 	do {
312251f4fe2SMin Li 		err = read_boot_status(idtcm, &status);
313251f4fe2SMin Li 		if (err)
314251f4fe2SMin Li 			return err;
315251f4fe2SMin Li 
316251f4fe2SMin Li 		if (status == 0xA0)
317251f4fe2SMin Li 			return 0;
318251f4fe2SMin Li 
319251f4fe2SMin Li 		msleep(100);
320251f4fe2SMin Li 		i--;
321251f4fe2SMin Li 
322251f4fe2SMin Li 	} while (i);
323251f4fe2SMin Li 
3241c49d3e9SVincent Cheng 	dev_warn(&idtcm->client->dev, "%s timed out", __func__);
325251f4fe2SMin Li 
326251f4fe2SMin Li 	return -EBUSY;
327251f4fe2SMin Li }
328251f4fe2SMin Li 
329797d3186SVincent Cheng static int read_sys_apll_status(struct idtcm *idtcm, u8 *status)
330797d3186SVincent Cheng {
331797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status,
332797d3186SVincent Cheng 			  sizeof(u8));
333797d3186SVincent Cheng }
334797d3186SVincent Cheng 
335797d3186SVincent Cheng static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status)
336797d3186SVincent Cheng {
337797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8));
338797d3186SVincent Cheng }
339797d3186SVincent Cheng 
340797d3186SVincent Cheng static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm)
341797d3186SVincent Cheng {
342797d3186SVincent Cheng 	unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS);
343797d3186SVincent Cheng 	u8 apll = 0;
344797d3186SVincent Cheng 	u8 dpll = 0;
345797d3186SVincent Cheng 	int err;
346797d3186SVincent Cheng 
347797d3186SVincent Cheng 	do {
348797d3186SVincent Cheng 		err = read_sys_apll_status(idtcm, &apll);
349797d3186SVincent Cheng 		if (err)
350797d3186SVincent Cheng 			return err;
351797d3186SVincent Cheng 
352797d3186SVincent Cheng 		err = read_sys_dpll_status(idtcm, &dpll);
353797d3186SVincent Cheng 		if (err)
354797d3186SVincent Cheng 			return err;
355797d3186SVincent Cheng 
356797d3186SVincent Cheng 		apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK;
357797d3186SVincent Cheng 		dpll &= DPLL_SYS_STATE_MASK;
358797d3186SVincent Cheng 
359797d3186SVincent Cheng 		if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED &&
360797d3186SVincent Cheng 		    dpll == DPLL_STATE_LOCKED) {
361797d3186SVincent Cheng 			return 0;
362797d3186SVincent Cheng 		} else if (dpll == DPLL_STATE_FREERUN ||
363797d3186SVincent Cheng 			   dpll == DPLL_STATE_HOLDOVER ||
364797d3186SVincent Cheng 			   dpll == DPLL_STATE_OPEN_LOOP) {
365797d3186SVincent Cheng 			dev_warn(&idtcm->client->dev,
366797d3186SVincent Cheng 				"No wait state: DPLL_SYS_STATE %d", dpll);
367797d3186SVincent Cheng 			return -EPERM;
368797d3186SVincent Cheng 		}
369797d3186SVincent Cheng 
370797d3186SVincent Cheng 		msleep(LOCK_POLL_INTERVAL_MS);
371797d3186SVincent Cheng 	} while (time_is_after_jiffies(timeout));
372797d3186SVincent Cheng 
373797d3186SVincent Cheng 	dev_warn(&idtcm->client->dev,
374797d3186SVincent Cheng 		 "%d ms lock timeout: SYS APLL Loss Lock %d  SYS DPLL state %d",
375797d3186SVincent Cheng 		 LOCK_TIMEOUT_MS, apll, dpll);
376797d3186SVincent Cheng 
377797d3186SVincent Cheng 	return -ETIME;
378797d3186SVincent Cheng }
379797d3186SVincent Cheng 
380797d3186SVincent Cheng static void wait_for_chip_ready(struct idtcm *idtcm)
381797d3186SVincent Cheng {
382797d3186SVincent Cheng 	if (wait_for_boot_status_ready(idtcm))
383797d3186SVincent Cheng 		dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0");
384797d3186SVincent Cheng 
385797d3186SVincent Cheng 	if (wait_for_sys_apll_dpll_lock(idtcm))
386797d3186SVincent Cheng 		dev_warn(&idtcm->client->dev,
387797d3186SVincent Cheng 			 "Continuing while SYS APLL/DPLL is not locked");
388797d3186SVincent Cheng }
389797d3186SVincent Cheng 
3903a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel,
3913a6ba7dcSVincent Cheng 			  struct timespec64 *ts)
3923a6ba7dcSVincent Cheng {
3933a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
3943a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
3957ea5fda2SMin Li 	u8 timeout = 10;
3963a6ba7dcSVincent Cheng 	u8 trigger;
3973a6ba7dcSVincent Cheng 	int err;
3983a6ba7dcSVincent Cheng 
3993a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
4003a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
4013a6ba7dcSVincent Cheng 	if (err)
4023a6ba7dcSVincent Cheng 		return err;
4033a6ba7dcSVincent Cheng 
4043a6ba7dcSVincent Cheng 	trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
4053a6ba7dcSVincent Cheng 	trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
4067ea5fda2SMin Li 	trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
4073a6ba7dcSVincent Cheng 
4083a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_read_primary,
4093a6ba7dcSVincent Cheng 			  TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
4107ea5fda2SMin Li 	if (err)
4117ea5fda2SMin Li 		return err;
4127ea5fda2SMin Li 
4137ea5fda2SMin Li 	/* wait trigger to be 0 */
4147ea5fda2SMin Li 	while (trigger & TOD_READ_TRIGGER_MASK) {
4157ea5fda2SMin Li 		if (idtcm->calculate_overhead_flag)
4167ea5fda2SMin Li 			idtcm->start_time = ktime_get_raw();
4177ea5fda2SMin Li 
4187ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_read_primary,
4197ea5fda2SMin Li 				 TOD_READ_PRIMARY_CMD, &trigger,
4207ea5fda2SMin Li 				 sizeof(trigger));
4213a6ba7dcSVincent Cheng 		if (err)
4223a6ba7dcSVincent Cheng 			return err;
4233a6ba7dcSVincent Cheng 
4247ea5fda2SMin Li 		if (--timeout == 0)
4257ea5fda2SMin Li 			return -EIO;
4267ea5fda2SMin Li 	}
4273a6ba7dcSVincent Cheng 
4283a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
4293a6ba7dcSVincent Cheng 			 TOD_READ_PRIMARY, buf, sizeof(buf));
4303a6ba7dcSVincent Cheng 	if (err)
4313a6ba7dcSVincent Cheng 		return err;
4323a6ba7dcSVincent Cheng 
4333a6ba7dcSVincent Cheng 	err = char_array_to_timespec(buf, sizeof(buf), ts);
4343a6ba7dcSVincent Cheng 
4353a6ba7dcSVincent Cheng 	return err;
4363a6ba7dcSVincent Cheng }
4373a6ba7dcSVincent Cheng 
4383a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm,
4393a6ba7dcSVincent Cheng 			    u8 pll,
4403a6ba7dcSVincent Cheng 			    u8 sync_src,
4413a6ba7dcSVincent Cheng 			    u8 qn,
4423a6ba7dcSVincent Cheng 			    u8 qn_plus_1)
4433a6ba7dcSVincent Cheng {
4443a6ba7dcSVincent Cheng 	int err;
4453a6ba7dcSVincent Cheng 	u8 val;
4463a6ba7dcSVincent Cheng 	u16 sync_ctrl0;
4473a6ba7dcSVincent Cheng 	u16 sync_ctrl1;
4487ea5fda2SMin Li 	u8 temp;
4493a6ba7dcSVincent Cheng 
4503a6ba7dcSVincent Cheng 	if ((qn == 0) && (qn_plus_1 == 0))
4513a6ba7dcSVincent Cheng 		return 0;
4523a6ba7dcSVincent Cheng 
4533a6ba7dcSVincent Cheng 	switch (pll) {
4543a6ba7dcSVincent Cheng 	case 0:
4553a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
4563a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
4573a6ba7dcSVincent Cheng 		break;
4583a6ba7dcSVincent Cheng 	case 1:
4593a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
4603a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
4613a6ba7dcSVincent Cheng 		break;
4623a6ba7dcSVincent Cheng 	case 2:
4633a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
4643a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
4653a6ba7dcSVincent Cheng 		break;
4663a6ba7dcSVincent Cheng 	case 3:
4673a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
4683a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
4693a6ba7dcSVincent Cheng 		break;
4703a6ba7dcSVincent Cheng 	case 4:
4713a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
4723a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
4733a6ba7dcSVincent Cheng 		break;
4743a6ba7dcSVincent Cheng 	case 5:
4753a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
4763a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
4773a6ba7dcSVincent Cheng 		break;
4783a6ba7dcSVincent Cheng 	case 6:
4793a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
4803a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
4813a6ba7dcSVincent Cheng 		break;
4823a6ba7dcSVincent Cheng 	case 7:
4833a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
4843a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
4853a6ba7dcSVincent Cheng 		break;
4863a6ba7dcSVincent Cheng 	default:
4873a6ba7dcSVincent Cheng 		return -EINVAL;
4883a6ba7dcSVincent Cheng 	}
4893a6ba7dcSVincent Cheng 
4903a6ba7dcSVincent Cheng 	val = SYNCTRL1_MASTER_SYNC_RST;
4913a6ba7dcSVincent Cheng 
4923a6ba7dcSVincent Cheng 	/* Place master sync in reset */
4933a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
4943a6ba7dcSVincent Cheng 	if (err)
4953a6ba7dcSVincent Cheng 		return err;
4963a6ba7dcSVincent Cheng 
4973a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
4983a6ba7dcSVincent Cheng 	if (err)
4993a6ba7dcSVincent Cheng 		return err;
5003a6ba7dcSVincent Cheng 
5013a6ba7dcSVincent Cheng 	/* Set sync trigger mask */
5023a6ba7dcSVincent Cheng 	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
5033a6ba7dcSVincent Cheng 
5043a6ba7dcSVincent Cheng 	if (qn)
5053a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
5063a6ba7dcSVincent Cheng 
5073a6ba7dcSVincent Cheng 	if (qn_plus_1)
5083a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
5093a6ba7dcSVincent Cheng 
5103a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5113a6ba7dcSVincent Cheng 	if (err)
5123a6ba7dcSVincent Cheng 		return err;
5133a6ba7dcSVincent Cheng 
5147ea5fda2SMin Li 	/* PLL5 can have OUT8 as second additional output. */
5157ea5fda2SMin Li 	if ((pll == 5) && (qn_plus_1 != 0)) {
5167ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
5177ea5fda2SMin Li 				 &temp, sizeof(temp));
5187ea5fda2SMin Li 		if (err)
5197ea5fda2SMin Li 			return err;
5207ea5fda2SMin Li 
5217ea5fda2SMin Li 		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
5227ea5fda2SMin Li 
5237ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5247ea5fda2SMin Li 				  &temp, sizeof(temp));
5257ea5fda2SMin Li 		if (err)
5267ea5fda2SMin Li 			return err;
5277ea5fda2SMin Li 
5287ea5fda2SMin Li 		temp |= Q9_TO_Q8_SYNC_TRIG;
5297ea5fda2SMin Li 
5307ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5317ea5fda2SMin Li 				  &temp, sizeof(temp));
5327ea5fda2SMin Li 		if (err)
5337ea5fda2SMin Li 			return err;
5347ea5fda2SMin Li 	}
5357ea5fda2SMin Li 
5367ea5fda2SMin Li 	/* PLL6 can have OUT11 as second additional output. */
5377ea5fda2SMin Li 	if ((pll == 6) && (qn_plus_1 != 0)) {
5387ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
5397ea5fda2SMin Li 				 &temp, sizeof(temp));
5407ea5fda2SMin Li 		if (err)
5417ea5fda2SMin Li 			return err;
5427ea5fda2SMin Li 
5437ea5fda2SMin Li 		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
5447ea5fda2SMin Li 
5457ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
5467ea5fda2SMin Li 				  &temp, sizeof(temp));
5477ea5fda2SMin Li 		if (err)
5487ea5fda2SMin Li 			return err;
5497ea5fda2SMin Li 
5507ea5fda2SMin Li 		temp |= Q10_TO_Q11_SYNC_TRIG;
5517ea5fda2SMin Li 
5527ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
5537ea5fda2SMin Li 				  &temp, sizeof(temp));
5547ea5fda2SMin Li 		if (err)
5557ea5fda2SMin Li 			return err;
5567ea5fda2SMin Li 	}
5577ea5fda2SMin Li 
5583a6ba7dcSVincent Cheng 	/* Place master sync out of reset */
5593a6ba7dcSVincent Cheng 	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
5603a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5613a6ba7dcSVincent Cheng 
5623a6ba7dcSVincent Cheng 	return err;
5633a6ba7dcSVincent Cheng }
5643a6ba7dcSVincent Cheng 
5657ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
5667ea5fda2SMin Li {
5677ea5fda2SMin Li 	int err = 0;
5687ea5fda2SMin Li 
5697ea5fda2SMin Li 	switch (tod_addr) {
5707ea5fda2SMin Li 	case TOD_0:
5717ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
5727ea5fda2SMin Li 		break;
5737ea5fda2SMin Li 	case TOD_1:
5747ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
5757ea5fda2SMin Li 		break;
5767ea5fda2SMin Li 	case TOD_2:
5777ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
5787ea5fda2SMin Li 		break;
5797ea5fda2SMin Li 	case TOD_3:
5807ea5fda2SMin Li 		*sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
5817ea5fda2SMin Li 		break;
5827ea5fda2SMin Li 	default:
5837ea5fda2SMin Li 		err = -EINVAL;
5847ea5fda2SMin Li 	}
5857ea5fda2SMin Li 
5867ea5fda2SMin Li 	return err;
5877ea5fda2SMin Li }
5887ea5fda2SMin Li 
5893a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel)
5903a6ba7dcSVincent Cheng {
5913a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
5923a6ba7dcSVincent Cheng 	u8 pll;
5933a6ba7dcSVincent Cheng 	u8 sync_src;
5943a6ba7dcSVincent Cheng 	u8 qn;
5953a6ba7dcSVincent Cheng 	u8 qn_plus_1;
5963a6ba7dcSVincent Cheng 	int err = 0;
5977ea5fda2SMin Li 	u8 out8_mux = 0;
5987ea5fda2SMin Li 	u8 out11_mux = 0;
5997ea5fda2SMin Li 	u8 temp;
6003a6ba7dcSVincent Cheng 	u16 output_mask = channel->output_mask;
6013a6ba7dcSVincent Cheng 
6027ea5fda2SMin Li 	err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
6037ea5fda2SMin Li 	if (err)
6047ea5fda2SMin Li 		return err;
6057ea5fda2SMin Li 
6067ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
6077ea5fda2SMin Li 			 &temp, sizeof(temp));
6087ea5fda2SMin Li 	if (err)
6097ea5fda2SMin Li 		return err;
6107ea5fda2SMin Li 
6117ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6127ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6137ea5fda2SMin Li 		out8_mux = 1;
6147ea5fda2SMin Li 
6157ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
6167ea5fda2SMin Li 			 &temp, sizeof(temp));
6177ea5fda2SMin Li 	if (err)
6187ea5fda2SMin Li 		return err;
6197ea5fda2SMin Li 
6207ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6217ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6227ea5fda2SMin Li 		out11_mux = 1;
6233a6ba7dcSVincent Cheng 
6243a6ba7dcSVincent Cheng 	for (pll = 0; pll < 8; pll++) {
6257ea5fda2SMin Li 		qn = 0;
6267ea5fda2SMin Li 		qn_plus_1 = 0;
6273a6ba7dcSVincent Cheng 
6283a6ba7dcSVincent Cheng 		if (pll < 4) {
6293a6ba7dcSVincent Cheng 			/* First 4 pll has 2 outputs */
6307ea5fda2SMin Li 			qn = output_mask & 0x1;
6317ea5fda2SMin Li 			output_mask = output_mask >> 1;
6323a6ba7dcSVincent Cheng 			qn_plus_1 = output_mask & 0x1;
6333a6ba7dcSVincent Cheng 			output_mask = output_mask >> 1;
6347ea5fda2SMin Li 		} else if (pll == 4) {
6357ea5fda2SMin Li 			if (out8_mux == 0) {
6367ea5fda2SMin Li 				qn = output_mask & 0x1;
6377ea5fda2SMin Li 				output_mask = output_mask >> 1;
6387ea5fda2SMin Li 			}
6397ea5fda2SMin Li 		} else if (pll == 5) {
6407ea5fda2SMin Li 			if (out8_mux) {
6417ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6427ea5fda2SMin Li 				output_mask = output_mask >> 1;
6437ea5fda2SMin Li 			}
6447ea5fda2SMin Li 			qn = output_mask & 0x1;
6457ea5fda2SMin Li 			output_mask = output_mask >> 1;
6467ea5fda2SMin Li 		} else if (pll == 6) {
6477ea5fda2SMin Li 			qn = output_mask & 0x1;
6487ea5fda2SMin Li 			output_mask = output_mask >> 1;
6497ea5fda2SMin Li 			if (out11_mux) {
6507ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6517ea5fda2SMin Li 				output_mask = output_mask >> 1;
6527ea5fda2SMin Li 			}
6537ea5fda2SMin Li 		} else if (pll == 7) {
6547ea5fda2SMin Li 			if (out11_mux == 0) {
6557ea5fda2SMin Li 				qn = output_mask & 0x1;
6567ea5fda2SMin Li 				output_mask = output_mask >> 1;
6577ea5fda2SMin Li 			}
6583a6ba7dcSVincent Cheng 		}
6593a6ba7dcSVincent Cheng 
6603a6ba7dcSVincent Cheng 		if ((qn != 0) || (qn_plus_1 != 0))
6613a6ba7dcSVincent Cheng 			err = _sync_pll_output(idtcm, pll, sync_src, qn,
6623a6ba7dcSVincent Cheng 					       qn_plus_1);
6633a6ba7dcSVincent Cheng 
6643a6ba7dcSVincent Cheng 		if (err)
6653a6ba7dcSVincent Cheng 			return err;
6663a6ba7dcSVincent Cheng 	}
6673a6ba7dcSVincent Cheng 
6683a6ba7dcSVincent Cheng 	return err;
6693a6ba7dcSVincent Cheng }
6703a6ba7dcSVincent Cheng 
6717ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
6723a6ba7dcSVincent Cheng 			       struct timespec64 const *ts,
6733a6ba7dcSVincent Cheng 			       enum hw_tod_write_trig_sel wr_trig)
6743a6ba7dcSVincent Cheng {
6753a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
6763a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
6773a6ba7dcSVincent Cheng 	u8 cmd;
6783a6ba7dcSVincent Cheng 	int err;
6793a6ba7dcSVincent Cheng 	struct timespec64 local_ts = *ts;
6803a6ba7dcSVincent Cheng 	s64 total_overhead_ns;
6813a6ba7dcSVincent Cheng 
6823a6ba7dcSVincent Cheng 	/* Configure HW TOD write trigger. */
6833a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6843a6ba7dcSVincent Cheng 			 &cmd, sizeof(cmd));
6853a6ba7dcSVincent Cheng 	if (err)
6863a6ba7dcSVincent Cheng 		return err;
6873a6ba7dcSVincent Cheng 
6883a6ba7dcSVincent Cheng 	cmd &= ~(0x0f);
6893a6ba7dcSVincent Cheng 	cmd |= wr_trig | 0x08;
6903a6ba7dcSVincent Cheng 
6913a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
6923a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
6933a6ba7dcSVincent Cheng 	if (err)
6943a6ba7dcSVincent Cheng 		return err;
6953a6ba7dcSVincent Cheng 
6963a6ba7dcSVincent Cheng 	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
6973a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6983a6ba7dcSVincent Cheng 		if (err)
6993a6ba7dcSVincent Cheng 			return err;
7003a6ba7dcSVincent Cheng 
7013a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
7023a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7033a6ba7dcSVincent Cheng 		if (err)
7043a6ba7dcSVincent Cheng 			return err;
7053a6ba7dcSVincent Cheng 	}
7063a6ba7dcSVincent Cheng 
7073a6ba7dcSVincent Cheng 	/* ARM HW TOD write trigger. */
7083a6ba7dcSVincent Cheng 	cmd &= ~(0x08);
7093a6ba7dcSVincent Cheng 
7103a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
7113a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
7123a6ba7dcSVincent Cheng 
7133a6ba7dcSVincent Cheng 	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
7143a6ba7dcSVincent Cheng 		if (idtcm->calculate_overhead_flag) {
7151ece2fbeSVincent Cheng 			/* Assumption: I2C @ 400KHz */
7167260d1c8SMin Li 			ktime_t diff = ktime_sub(ktime_get_raw(),
7177260d1c8SMin Li 						 idtcm->start_time);
7187260d1c8SMin Li 			total_overhead_ns =  ktime_to_ns(diff)
7193a6ba7dcSVincent Cheng 					     + idtcm->tod_write_overhead_ns
7203a6ba7dcSVincent Cheng 					     + SETTIME_CORRECTION;
7213a6ba7dcSVincent Cheng 
7223a6ba7dcSVincent Cheng 			timespec64_add_ns(&local_ts, total_overhead_ns);
7233a6ba7dcSVincent Cheng 
7243a6ba7dcSVincent Cheng 			idtcm->calculate_overhead_flag = 0;
7253a6ba7dcSVincent Cheng 		}
7263a6ba7dcSVincent Cheng 
7273a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7283a6ba7dcSVincent Cheng 		if (err)
7293a6ba7dcSVincent Cheng 			return err;
7303a6ba7dcSVincent Cheng 
7313a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
7323a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7333a6ba7dcSVincent Cheng 	}
7343a6ba7dcSVincent Cheng 
7353a6ba7dcSVincent Cheng 	return err;
7363a6ba7dcSVincent Cheng }
7373a6ba7dcSVincent Cheng 
7387ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
7397ea5fda2SMin Li 				    struct timespec64 const *ts,
7407ea5fda2SMin Li 				    enum scsr_tod_write_trig_sel wr_trig,
7417ea5fda2SMin Li 				    enum scsr_tod_write_type_sel wr_type)
7427ea5fda2SMin Li {
7437ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
7447ea5fda2SMin Li 	unsigned char buf[TOD_BYTE_COUNT], cmd;
7457ea5fda2SMin Li 	struct timespec64 local_ts = *ts;
7467ea5fda2SMin Li 	int err, count = 0;
7477ea5fda2SMin Li 
7487ea5fda2SMin Li 	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
7497ea5fda2SMin Li 
7507ea5fda2SMin Li 	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7517ea5fda2SMin Li 	if (err)
7527ea5fda2SMin Li 		return err;
7537ea5fda2SMin Li 
7547ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
7557ea5fda2SMin Li 			  buf, sizeof(buf));
7567ea5fda2SMin Li 	if (err)
7577ea5fda2SMin Li 		return err;
7587ea5fda2SMin Li 
7597ea5fda2SMin Li 	/* Trigger the write operation. */
7607ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7617ea5fda2SMin Li 			 &cmd, sizeof(cmd));
7627ea5fda2SMin Li 	if (err)
7637ea5fda2SMin Li 		return err;
7647ea5fda2SMin Li 
7657ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
7667ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
7677ea5fda2SMin Li 	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
7687ea5fda2SMin Li 	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
7697ea5fda2SMin Li 
7707ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
7717ea5fda2SMin Li 			   &cmd, sizeof(cmd));
7727ea5fda2SMin Li 	if (err)
7737ea5fda2SMin Li 		return err;
7747ea5fda2SMin Li 
7757ea5fda2SMin Li 	/* Wait for the operation to complete. */
7767ea5fda2SMin Li 	while (1) {
7777ea5fda2SMin Li 		/* pps trigger takes up to 1 sec to complete */
7787ea5fda2SMin Li 		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
7797ea5fda2SMin Li 			msleep(50);
7807ea5fda2SMin Li 
7817ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7827ea5fda2SMin Li 				 &cmd, sizeof(cmd));
7837ea5fda2SMin Li 		if (err)
7847ea5fda2SMin Li 			return err;
7857ea5fda2SMin Li 
786251f4fe2SMin Li 		if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
7877ea5fda2SMin Li 			break;
7887ea5fda2SMin Li 
7897ea5fda2SMin Li 		if (++count > 20) {
7907ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
7911c49d3e9SVincent Cheng 				"Timed out waiting for the write counter");
7927ea5fda2SMin Li 			return -EIO;
7937ea5fda2SMin Li 		}
7947ea5fda2SMin Li 	}
7957ea5fda2SMin Li 
7967ea5fda2SMin Li 	return 0;
7977ea5fda2SMin Li }
7987ea5fda2SMin Li 
7997260d1c8SMin Li static int get_output_base_addr(u8 outn)
8007260d1c8SMin Li {
8017260d1c8SMin Li 	int base;
8027260d1c8SMin Li 
8037260d1c8SMin Li 	switch (outn) {
8047260d1c8SMin Li 	case 0:
8057260d1c8SMin Li 		base = OUTPUT_0;
8067260d1c8SMin Li 		break;
8077260d1c8SMin Li 	case 1:
8087260d1c8SMin Li 		base = OUTPUT_1;
8097260d1c8SMin Li 		break;
8107260d1c8SMin Li 	case 2:
8117260d1c8SMin Li 		base = OUTPUT_2;
8127260d1c8SMin Li 		break;
8137260d1c8SMin Li 	case 3:
8147260d1c8SMin Li 		base = OUTPUT_3;
8157260d1c8SMin Li 		break;
8167260d1c8SMin Li 	case 4:
8177260d1c8SMin Li 		base = OUTPUT_4;
8187260d1c8SMin Li 		break;
8197260d1c8SMin Li 	case 5:
8207260d1c8SMin Li 		base = OUTPUT_5;
8217260d1c8SMin Li 		break;
8227260d1c8SMin Li 	case 6:
8237260d1c8SMin Li 		base = OUTPUT_6;
8247260d1c8SMin Li 		break;
8257260d1c8SMin Li 	case 7:
8267260d1c8SMin Li 		base = OUTPUT_7;
8277260d1c8SMin Li 		break;
8287260d1c8SMin Li 	case 8:
8297260d1c8SMin Li 		base = OUTPUT_8;
8307260d1c8SMin Li 		break;
8317260d1c8SMin Li 	case 9:
8327260d1c8SMin Li 		base = OUTPUT_9;
8337260d1c8SMin Li 		break;
8347260d1c8SMin Li 	case 10:
8357260d1c8SMin Li 		base = OUTPUT_10;
8367260d1c8SMin Li 		break;
8377260d1c8SMin Li 	case 11:
8387260d1c8SMin Li 		base = OUTPUT_11;
8397260d1c8SMin Li 		break;
8407260d1c8SMin Li 	default:
8417260d1c8SMin Li 		base = -EINVAL;
8427260d1c8SMin Li 	}
8437260d1c8SMin Li 
8447260d1c8SMin Li 	return base;
8457260d1c8SMin Li }
8467260d1c8SMin Li 
847da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
848251f4fe2SMin Li 				     struct timespec64 const *ts)
8493a6ba7dcSVincent Cheng {
8503a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8513a6ba7dcSVincent Cheng 	int err;
8523a6ba7dcSVincent Cheng 
853251f4fe2SMin Li 	err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
8547ea5fda2SMin Li 	if (err) {
8557ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
8561c49d3e9SVincent Cheng 			"%s: Set HW ToD failed", __func__);
8573a6ba7dcSVincent Cheng 		return err;
8587ea5fda2SMin Li 	}
8593a6ba7dcSVincent Cheng 
8607ea5fda2SMin Li 	return idtcm_sync_pps_output(channel);
8617ea5fda2SMin Li }
8623a6ba7dcSVincent Cheng 
863da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel,
8647ea5fda2SMin Li 			  struct timespec64 const *ts,
8657ea5fda2SMin Li 			  enum scsr_tod_write_type_sel wr_type)
8667ea5fda2SMin Li {
8677ea5fda2SMin Li 	return _idtcm_set_dpll_scsr_tod(channel, ts,
8687ea5fda2SMin Li 					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
8697ea5fda2SMin Li 					wr_type);
8703a6ba7dcSVincent Cheng }
8713a6ba7dcSVincent Cheng 
8723a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
8733a6ba7dcSVincent Cheng 					  s32 offset_ns)
8743a6ba7dcSVincent Cheng {
8753a6ba7dcSVincent Cheng 	int err;
8763a6ba7dcSVincent Cheng 	int i;
8773a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8783a6ba7dcSVincent Cheng 	u8 buf[4];
8793a6ba7dcSVincent Cheng 
8803a6ba7dcSVincent Cheng 	for (i = 0; i < 4; i++) {
8813a6ba7dcSVincent Cheng 		buf[i] = 0xff & (offset_ns);
8823a6ba7dcSVincent Cheng 		offset_ns >>= 8;
8833a6ba7dcSVincent Cheng 	}
8843a6ba7dcSVincent Cheng 
8853a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
8863a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
8873a6ba7dcSVincent Cheng 
8883a6ba7dcSVincent Cheng 	return err;
8893a6ba7dcSVincent Cheng }
8903a6ba7dcSVincent Cheng 
8913a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
8923a6ba7dcSVincent Cheng 					       u32 max_ffo_ppb)
8933a6ba7dcSVincent Cheng {
8943a6ba7dcSVincent Cheng 	int err;
8953a6ba7dcSVincent Cheng 	u8 i;
8963a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8973a6ba7dcSVincent Cheng 	u8 buf[3];
8983a6ba7dcSVincent Cheng 
8993a6ba7dcSVincent Cheng 	if (max_ffo_ppb & 0xff000000)
9003a6ba7dcSVincent Cheng 		max_ffo_ppb = 0;
9013a6ba7dcSVincent Cheng 
9023a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
9033a6ba7dcSVincent Cheng 		buf[i] = 0xff & (max_ffo_ppb);
9043a6ba7dcSVincent Cheng 		max_ffo_ppb >>= 8;
9053a6ba7dcSVincent Cheng 	}
9063a6ba7dcSVincent Cheng 
9073a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9083a6ba7dcSVincent Cheng 			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
9093a6ba7dcSVincent Cheng 
9103a6ba7dcSVincent Cheng 	return err;
9113a6ba7dcSVincent Cheng }
9123a6ba7dcSVincent Cheng 
9133a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
9143a6ba7dcSVincent Cheng {
9153a6ba7dcSVincent Cheng 	int err;
9163a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9173a6ba7dcSVincent Cheng 	u8 buf;
9183a6ba7dcSVincent Cheng 
9193a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
9203a6ba7dcSVincent Cheng 			 &buf, sizeof(buf));
9213a6ba7dcSVincent Cheng 	if (err)
9223a6ba7dcSVincent Cheng 		return err;
9233a6ba7dcSVincent Cheng 
9243a6ba7dcSVincent Cheng 	if (buf == 0) {
9253a6ba7dcSVincent Cheng 		buf = 0x01;
9263a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9273a6ba7dcSVincent Cheng 				  PULL_IN_CTRL, &buf, sizeof(buf));
9283a6ba7dcSVincent Cheng 	} else {
9293a6ba7dcSVincent Cheng 		err = -EBUSY;
9303a6ba7dcSVincent Cheng 	}
9313a6ba7dcSVincent Cheng 
9323a6ba7dcSVincent Cheng 	return err;
9333a6ba7dcSVincent Cheng }
9343a6ba7dcSVincent Cheng 
9353a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
9363a6ba7dcSVincent Cheng 				  s32 offset_ns,
9373a6ba7dcSVincent Cheng 				  u32 max_ffo_ppb)
9383a6ba7dcSVincent Cheng {
9393a6ba7dcSVincent Cheng 	int err;
9403a6ba7dcSVincent Cheng 
9413a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
9423a6ba7dcSVincent Cheng 	if (err)
9433a6ba7dcSVincent Cheng 		return err;
9443a6ba7dcSVincent Cheng 
9453a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
9463a6ba7dcSVincent Cheng 	if (err)
9473a6ba7dcSVincent Cheng 		return err;
9483a6ba7dcSVincent Cheng 
9493a6ba7dcSVincent Cheng 	err = idtcm_start_phase_pull_in(channel);
9503a6ba7dcSVincent Cheng 
9513a6ba7dcSVincent Cheng 	return err;
9523a6ba7dcSVincent Cheng }
9533a6ba7dcSVincent Cheng 
9547ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel)
9557ea5fda2SMin Li {
9567ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
9577ea5fda2SMin Li 	s64 current_ns = 0;
9587ea5fda2SMin Li 	s64 lowest_ns = 0;
9597ea5fda2SMin Li 	int err;
9607ea5fda2SMin Li 	u8 i;
9617ea5fda2SMin Li 	ktime_t start;
9627ea5fda2SMin Li 	ktime_t stop;
9637260d1c8SMin Li 	ktime_t diff;
9647ea5fda2SMin Li 
9657ea5fda2SMin Li 	char buf[TOD_BYTE_COUNT] = {0};
9667ea5fda2SMin Li 
9677ea5fda2SMin Li 	/* Set page offset */
9687ea5fda2SMin Li 	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
9697ea5fda2SMin Li 		    buf, sizeof(buf));
9707ea5fda2SMin Li 
9717ea5fda2SMin Li 	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
9727ea5fda2SMin Li 		start = ktime_get_raw();
9737ea5fda2SMin Li 
9747ea5fda2SMin Li 		err = idtcm_write(idtcm, channel->hw_dpll_n,
9757ea5fda2SMin Li 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
9767ea5fda2SMin Li 		if (err)
9777ea5fda2SMin Li 			return err;
9787ea5fda2SMin Li 
9797ea5fda2SMin Li 		stop = ktime_get_raw();
9807ea5fda2SMin Li 
9817260d1c8SMin Li 		diff = ktime_sub(stop, start);
9827260d1c8SMin Li 
9837260d1c8SMin Li 		current_ns = ktime_to_ns(diff);
9847ea5fda2SMin Li 
9857ea5fda2SMin Li 		if (i == 0) {
9867ea5fda2SMin Li 			lowest_ns = current_ns;
9877ea5fda2SMin Li 		} else {
9887ea5fda2SMin Li 			if (current_ns < lowest_ns)
9897ea5fda2SMin Li 				lowest_ns = current_ns;
9907ea5fda2SMin Li 		}
9917ea5fda2SMin Li 	}
9927ea5fda2SMin Li 
9937ea5fda2SMin Li 	idtcm->tod_write_overhead_ns = lowest_ns;
9947ea5fda2SMin Li 
9957ea5fda2SMin Li 	return err;
9967ea5fda2SMin Li }
9977ea5fda2SMin Li 
998da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
9993a6ba7dcSVincent Cheng {
10003a6ba7dcSVincent Cheng 	int err;
10013a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
10023a6ba7dcSVincent Cheng 	struct timespec64 ts;
10033a6ba7dcSVincent Cheng 	s64 now;
10043a6ba7dcSVincent Cheng 
1005da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
10063a6ba7dcSVincent Cheng 		err = idtcm_do_phase_pull_in(channel, delta, 0);
10073a6ba7dcSVincent Cheng 	} else {
10083a6ba7dcSVincent Cheng 		idtcm->calculate_overhead_flag = 1;
10093a6ba7dcSVincent Cheng 
10107ea5fda2SMin Li 		err = set_tod_write_overhead(channel);
10117ea5fda2SMin Li 		if (err)
10127ea5fda2SMin Li 			return err;
10137ea5fda2SMin Li 
10143a6ba7dcSVincent Cheng 		err = _idtcm_gettime(channel, &ts);
10153a6ba7dcSVincent Cheng 		if (err)
10163a6ba7dcSVincent Cheng 			return err;
10173a6ba7dcSVincent Cheng 
10183a6ba7dcSVincent Cheng 		now = timespec64_to_ns(&ts);
10193a6ba7dcSVincent Cheng 		now += delta;
10203a6ba7dcSVincent Cheng 
10213a6ba7dcSVincent Cheng 		ts = ns_to_timespec64(now);
10223a6ba7dcSVincent Cheng 
1023da948233SMin Li 		err = _idtcm_settime_deprecated(channel, &ts);
10243a6ba7dcSVincent Cheng 	}
10253a6ba7dcSVincent Cheng 
10263a6ba7dcSVincent Cheng 	return err;
10273a6ba7dcSVincent Cheng }
10283a6ba7dcSVincent Cheng 
10293a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm)
10303a6ba7dcSVincent Cheng {
10313a6ba7dcSVincent Cheng 	u8 byte = SM_RESET_CMD;
1032251f4fe2SMin Li 	u32 status = 0;
1033251f4fe2SMin Li 	int err;
1034251f4fe2SMin Li 	u8 i;
1035251f4fe2SMin Li 
1036251f4fe2SMin Li 	clear_boot_status(idtcm);
10373a6ba7dcSVincent Cheng 
10383a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
10393a6ba7dcSVincent Cheng 
1040251f4fe2SMin Li 	if (!err) {
1041251f4fe2SMin Li 		for (i = 0; i < 30; i++) {
1042251f4fe2SMin Li 			msleep_interruptible(100);
1043251f4fe2SMin Li 			read_boot_status(idtcm, &status);
1044251f4fe2SMin Li 
1045251f4fe2SMin Li 			if (status == 0xA0) {
1046251f4fe2SMin Li 				dev_dbg(&idtcm->client->dev,
10471c49d3e9SVincent Cheng 					"SM_RESET completed in %d ms", i * 100);
1048251f4fe2SMin Li 				break;
1049251f4fe2SMin Li 			}
1050251f4fe2SMin Li 		}
1051251f4fe2SMin Li 
1052251f4fe2SMin Li 		if (!status)
10531c49d3e9SVincent Cheng 			dev_err(&idtcm->client->dev,
10541c49d3e9SVincent Cheng 				"Timed out waiting for CM_RESET to complete");
1055251f4fe2SMin Li 	}
10563a6ba7dcSVincent Cheng 
10573a6ba7dcSVincent Cheng 	return err;
10583a6ba7dcSVincent Cheng }
10593a6ba7dcSVincent Cheng 
10603a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
10613a6ba7dcSVincent Cheng {
10621ece2fbeSVincent Cheng 	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
10633a6ba7dcSVincent Cheng }
10643a6ba7dcSVincent Cheng 
10653a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
10663a6ba7dcSVincent Cheng {
10673a6ba7dcSVincent Cheng 	int err;
10683a6ba7dcSVincent Cheng 	u8 buf[2] = {0};
10693a6ba7dcSVincent Cheng 
10703a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
10713a6ba7dcSVincent Cheng 
10723a6ba7dcSVincent Cheng 	*product_id = (buf[1] << 8) | buf[0];
10733a6ba7dcSVincent Cheng 
10743a6ba7dcSVincent Cheng 	return err;
10753a6ba7dcSVincent Cheng }
10763a6ba7dcSVincent Cheng 
10773a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
10783a6ba7dcSVincent Cheng {
10793a6ba7dcSVincent Cheng 	int err;
10803a6ba7dcSVincent Cheng 	u8 buf = 0;
10813a6ba7dcSVincent Cheng 
10823a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
10833a6ba7dcSVincent Cheng 
10843a6ba7dcSVincent Cheng 	*major = buf >> 1;
10853a6ba7dcSVincent Cheng 
10863a6ba7dcSVincent Cheng 	return err;
10873a6ba7dcSVincent Cheng }
10883a6ba7dcSVincent Cheng 
10893a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
10903a6ba7dcSVincent Cheng {
10913a6ba7dcSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
10923a6ba7dcSVincent Cheng }
10933a6ba7dcSVincent Cheng 
10943a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
10953a6ba7dcSVincent Cheng {
10963a6ba7dcSVincent Cheng 	return idtcm_read(idtcm,
10973a6ba7dcSVincent Cheng 			  GENERAL_STATUS,
10983a6ba7dcSVincent Cheng 			  HOTFIX_REL,
10993a6ba7dcSVincent Cheng 			  hotfix,
11003a6ba7dcSVincent Cheng 			  sizeof(u8));
11013a6ba7dcSVincent Cheng }
11023a6ba7dcSVincent Cheng 
11031ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
11041ece2fbeSVincent Cheng 					     u8 *config_select)
11053a6ba7dcSVincent Cheng {
11061ece2fbeSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
11071ece2fbeSVincent Cheng 			  config_select, sizeof(u8));
11083a6ba7dcSVincent Cheng }
11093a6ba7dcSVincent Cheng 
11103a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
11113a6ba7dcSVincent Cheng {
11123a6ba7dcSVincent Cheng 	int err = 0;
11133a6ba7dcSVincent Cheng 
11143a6ba7dcSVincent Cheng 	switch (addr) {
11157ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR:
11163a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[0].output_mask, val);
11173a6ba7dcSVincent Cheng 		break;
11187ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
11193a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[0].output_mask, val);
11203a6ba7dcSVincent Cheng 		break;
11217ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR:
11223a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[1].output_mask, val);
11233a6ba7dcSVincent Cheng 		break;
11247ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
11253a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[1].output_mask, val);
11263a6ba7dcSVincent Cheng 		break;
11277ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR:
11283a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[2].output_mask, val);
11293a6ba7dcSVincent Cheng 		break;
11307ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
11313a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[2].output_mask, val);
11323a6ba7dcSVincent Cheng 		break;
11337ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR:
11343a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[3].output_mask, val);
11353a6ba7dcSVincent Cheng 		break;
11367ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
11373a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[3].output_mask, val);
11383a6ba7dcSVincent Cheng 		break;
11393a6ba7dcSVincent Cheng 	default:
11407ea5fda2SMin Li 		err = -EFAULT; /* Bad address */;
11413a6ba7dcSVincent Cheng 		break;
11423a6ba7dcSVincent Cheng 	}
11433a6ba7dcSVincent Cheng 
11443a6ba7dcSVincent Cheng 	return err;
11453a6ba7dcSVincent Cheng }
11463a6ba7dcSVincent Cheng 
11477ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
11487ea5fda2SMin Li {
11497ea5fda2SMin Li 	if (index >= MAX_TOD) {
11501c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "ToD%d not supported", index);
11517ea5fda2SMin Li 		return -EINVAL;
11527ea5fda2SMin Li 	}
11537ea5fda2SMin Li 
11547ea5fda2SMin Li 	if (pll >= MAX_PLL) {
11551c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "Pll%d not supported", pll);
11567ea5fda2SMin Li 		return -EINVAL;
11577ea5fda2SMin Li 	}
11587ea5fda2SMin Li 
11597ea5fda2SMin Li 	idtcm->channel[index].pll = pll;
11607ea5fda2SMin Li 
11617ea5fda2SMin Li 	return 0;
11627ea5fda2SMin Li }
11637ea5fda2SMin Li 
11643a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm,
11653a6ba7dcSVincent Cheng 			       u16 regaddr,
11663a6ba7dcSVincent Cheng 			       u8 val)
11673a6ba7dcSVincent Cheng {
11683a6ba7dcSVincent Cheng 	int err = 0;
11693a6ba7dcSVincent Cheng 
11707ea5fda2SMin Li 	switch (regaddr) {
11717ea5fda2SMin Li 	case TOD_MASK_ADDR:
11727ea5fda2SMin Li 		if ((val & 0xf0) || !(val & 0x0f)) {
11731c49d3e9SVincent Cheng 			dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val);
11747ea5fda2SMin Li 			err = -EINVAL;
11757ea5fda2SMin Li 		} else {
11767ea5fda2SMin Li 			idtcm->tod_mask = val;
11777ea5fda2SMin Li 		}
11787ea5fda2SMin Li 		break;
11797ea5fda2SMin Li 	case TOD0_PTP_PLL_ADDR:
11807ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 0, val);
11817ea5fda2SMin Li 		break;
11827ea5fda2SMin Li 	case TOD1_PTP_PLL_ADDR:
11837ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 1, val);
11847ea5fda2SMin Li 		break;
11857ea5fda2SMin Li 	case TOD2_PTP_PLL_ADDR:
11867ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 2, val);
11877ea5fda2SMin Li 		break;
11887ea5fda2SMin Li 	case TOD3_PTP_PLL_ADDR:
11897ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 3, val);
11907ea5fda2SMin Li 		break;
11917ea5fda2SMin Li 	default:
11927ea5fda2SMin Li 		err = set_pll_output_mask(idtcm, regaddr, val);
11937ea5fda2SMin Li 		break;
11943a6ba7dcSVincent Cheng 	}
11953a6ba7dcSVincent Cheng 
11963a6ba7dcSVincent Cheng 	return err;
11973a6ba7dcSVincent Cheng }
11983a6ba7dcSVincent Cheng 
11997ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm)
12003a6ba7dcSVincent Cheng {
12013a6ba7dcSVincent Cheng 	u8 i;
12023a6ba7dcSVincent Cheng 	u8 mask;
12033a6ba7dcSVincent Cheng 
12041c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask);
12053a6ba7dcSVincent Cheng 
12067ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
12073a6ba7dcSVincent Cheng 		mask = 1 << i;
12083a6ba7dcSVincent Cheng 
12097ea5fda2SMin Li 		if (mask & idtcm->tod_mask)
12103a6ba7dcSVincent Cheng 			dev_dbg(&idtcm->client->dev,
12111c49d3e9SVincent Cheng 				"TOD%d pll = %d    output_mask = 0x%04x",
12127ea5fda2SMin Li 				i, idtcm->channel[i].pll,
12137ea5fda2SMin Li 				idtcm->channel[i].output_mask);
12143a6ba7dcSVincent Cheng 	}
12153a6ba7dcSVincent Cheng }
12163a6ba7dcSVincent Cheng 
12173a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm,
12183a6ba7dcSVincent Cheng 			       struct device *dev)
12193a6ba7dcSVincent Cheng {
12207ea5fda2SMin Li 	char fname[128] = FW_FILENAME;
12213a6ba7dcSVincent Cheng 	const struct firmware *fw;
12223a6ba7dcSVincent Cheng 	struct idtcm_fwrc *rec;
12233a6ba7dcSVincent Cheng 	u32 regaddr;
12243a6ba7dcSVincent Cheng 	int err;
12253a6ba7dcSVincent Cheng 	s32 len;
12263a6ba7dcSVincent Cheng 	u8 val;
12273a6ba7dcSVincent Cheng 	u8 loaddr;
12283a6ba7dcSVincent Cheng 
12297ea5fda2SMin Li 	if (firmware) /* module parameter */
12307ea5fda2SMin Li 		snprintf(fname, sizeof(fname), "%s", firmware);
12313a6ba7dcSVincent Cheng 
12321c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname);
12333a6ba7dcSVincent Cheng 
12347ea5fda2SMin Li 	err = request_firmware(&fw, fname, dev);
12357ea5fda2SMin Li 	if (err) {
12367ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
12371c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
12383a6ba7dcSVincent Cheng 		return err;
12397ea5fda2SMin Li 	}
12403a6ba7dcSVincent Cheng 
12411c49d3e9SVincent Cheng 	dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size);
12423a6ba7dcSVincent Cheng 
12433a6ba7dcSVincent Cheng 	rec = (struct idtcm_fwrc *) fw->data;
12443a6ba7dcSVincent Cheng 
1245251f4fe2SMin Li 	if (contains_full_configuration(fw))
12463a6ba7dcSVincent Cheng 		idtcm_state_machine_reset(idtcm);
12473a6ba7dcSVincent Cheng 
12483a6ba7dcSVincent Cheng 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
12493a6ba7dcSVincent Cheng 		if (rec->reserved) {
12503a6ba7dcSVincent Cheng 			dev_err(&idtcm->client->dev,
12511c49d3e9SVincent Cheng 				"bad firmware, reserved field non-zero");
12523a6ba7dcSVincent Cheng 			err = -EINVAL;
12533a6ba7dcSVincent Cheng 		} else {
12543a6ba7dcSVincent Cheng 			regaddr = rec->hiaddr << 8;
12553a6ba7dcSVincent Cheng 			regaddr |= rec->loaddr;
12563a6ba7dcSVincent Cheng 
12573a6ba7dcSVincent Cheng 			val = rec->value;
12583a6ba7dcSVincent Cheng 			loaddr = rec->loaddr;
12593a6ba7dcSVincent Cheng 
12603a6ba7dcSVincent Cheng 			rec++;
12613a6ba7dcSVincent Cheng 
12623a6ba7dcSVincent Cheng 			err = check_and_set_masks(idtcm, regaddr, val);
12633a6ba7dcSVincent Cheng 		}
12643a6ba7dcSVincent Cheng 
12657ea5fda2SMin Li 		if (err != -EINVAL) {
12667ea5fda2SMin Li 			err = 0;
12677ea5fda2SMin Li 
12683a6ba7dcSVincent Cheng 			/* Top (status registers) and bottom are read-only */
12693a6ba7dcSVincent Cheng 			if ((regaddr < GPIO_USER_CONTROL)
12703a6ba7dcSVincent Cheng 			    || (regaddr >= SCRATCH))
12713a6ba7dcSVincent Cheng 				continue;
12723a6ba7dcSVincent Cheng 
12733a6ba7dcSVincent Cheng 			/* Page size 128, last 4 bytes of page skipped */
12743a6ba7dcSVincent Cheng 			if (((loaddr > 0x7b) && (loaddr <= 0x7f))
12753e14462fSYang Yingliang 			     || loaddr > 0xfb)
12763a6ba7dcSVincent Cheng 				continue;
12773a6ba7dcSVincent Cheng 
12783a6ba7dcSVincent Cheng 			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
12793a6ba7dcSVincent Cheng 		}
12803a6ba7dcSVincent Cheng 
12813a6ba7dcSVincent Cheng 		if (err)
12823a6ba7dcSVincent Cheng 			goto out;
12833a6ba7dcSVincent Cheng 	}
12843a6ba7dcSVincent Cheng 
12857ea5fda2SMin Li 	display_pll_and_masks(idtcm);
12863a6ba7dcSVincent Cheng 
12873a6ba7dcSVincent Cheng out:
12883a6ba7dcSVincent Cheng 	release_firmware(fw);
12893a6ba7dcSVincent Cheng 	return err;
12903a6ba7dcSVincent Cheng }
12913a6ba7dcSVincent Cheng 
12927ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel,
12937ea5fda2SMin Li 			       bool enable, unsigned int outn)
12943a6ba7dcSVincent Cheng {
12953a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
12967260d1c8SMin Li 	int base;
12973a6ba7dcSVincent Cheng 	int err;
12987ea5fda2SMin Li 	u8 val;
12993a6ba7dcSVincent Cheng 
13007260d1c8SMin Li 	base = get_output_base_addr(outn);
13017260d1c8SMin Li 
13027260d1c8SMin Li 	if (!(base > 0)) {
13037260d1c8SMin Li 		dev_err(&idtcm->client->dev,
13047260d1c8SMin Li 			"%s - Unsupported out%d", __func__, outn);
13057260d1c8SMin Li 		return base;
13067260d1c8SMin Li 	}
13077260d1c8SMin Li 
13087260d1c8SMin Li 	err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13093a6ba7dcSVincent Cheng 	if (err)
13103a6ba7dcSVincent Cheng 		return err;
13113a6ba7dcSVincent Cheng 
13123a6ba7dcSVincent Cheng 	if (enable)
13133a6ba7dcSVincent Cheng 		val |= SQUELCH_DISABLE;
13143a6ba7dcSVincent Cheng 	else
13153a6ba7dcSVincent Cheng 		val &= ~SQUELCH_DISABLE;
13163a6ba7dcSVincent Cheng 
13177260d1c8SMin Li 	return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13187ea5fda2SMin Li }
13197ea5fda2SMin Li 
13207ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel,
13217ea5fda2SMin Li 				    bool enable)
13227ea5fda2SMin Li {
13237ea5fda2SMin Li 	u16 mask;
13247ea5fda2SMin Li 	int err;
13257ea5fda2SMin Li 	u8 outn;
13267ea5fda2SMin Li 
13277ea5fda2SMin Li 	mask = channel->output_mask;
13287ea5fda2SMin Li 	outn = 0;
13297ea5fda2SMin Li 
13307ea5fda2SMin Li 	while (mask) {
13317ea5fda2SMin Li 		if (mask & 0x1) {
13327ea5fda2SMin Li 			err = idtcm_output_enable(channel, enable, outn);
13333a6ba7dcSVincent Cheng 			if (err)
13343a6ba7dcSVincent Cheng 				return err;
13357ea5fda2SMin Li 		}
13367ea5fda2SMin Li 
13377ea5fda2SMin Li 		mask >>= 0x1;
13387ea5fda2SMin Li 		outn++;
13397ea5fda2SMin Li 	}
13403a6ba7dcSVincent Cheng 
13413a6ba7dcSVincent Cheng 	return 0;
13423a6ba7dcSVincent Cheng }
13433a6ba7dcSVincent Cheng 
13447ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel,
13457ea5fda2SMin Li 			       bool enable,
13467ea5fda2SMin Li 			       struct ptp_perout_request *perout)
13477ea5fda2SMin Li {
1348e8b4d8b5SVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13497ea5fda2SMin Li 	unsigned int flags = perout->flags;
1350e8b4d8b5SVincent Cheng 	struct timespec64 ts = {0, 0};
1351e8b4d8b5SVincent Cheng 	int err;
13527ea5fda2SMin Li 
13537ea5fda2SMin Li 	if (flags == PEROUT_ENABLE_OUTPUT_MASK)
1354e8b4d8b5SVincent Cheng 		err = idtcm_output_mask_enable(channel, enable);
1355e8b4d8b5SVincent Cheng 	else
1356e8b4d8b5SVincent Cheng 		err = idtcm_output_enable(channel, enable, perout->index);
13577ea5fda2SMin Li 
1358e8b4d8b5SVincent Cheng 	if (err) {
1359e8b4d8b5SVincent Cheng 		dev_err(&idtcm->client->dev, "Unable to set output enable");
1360e8b4d8b5SVincent Cheng 		return err;
1361e8b4d8b5SVincent Cheng 	}
1362e8b4d8b5SVincent Cheng 
1363e8b4d8b5SVincent Cheng 	/* Align output to internal 1 PPS */
1364e8b4d8b5SVincent Cheng 	return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS);
13657ea5fda2SMin Li }
13667ea5fda2SMin Li 
13677260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel,
13687260d1c8SMin Li 			      enum pll_mode *pll_mode)
13697260d1c8SMin Li {
13707260d1c8SMin Li 	struct idtcm *idtcm = channel->idtcm;
13717260d1c8SMin Li 	int err;
13727260d1c8SMin Li 	u8 dpll_mode;
13737260d1c8SMin Li 
13747260d1c8SMin Li 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13757260d1c8SMin Li 			 &dpll_mode, sizeof(dpll_mode));
13767260d1c8SMin Li 	if (err)
13777260d1c8SMin Li 		return err;
13787260d1c8SMin Li 
13797260d1c8SMin Li 	*pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
13807260d1c8SMin Li 
13817260d1c8SMin Li 	return 0;
13827260d1c8SMin Li }
13837260d1c8SMin Li 
13843a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel,
13853a6ba7dcSVincent Cheng 			      enum pll_mode pll_mode)
13863a6ba7dcSVincent Cheng {
13873a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13883a6ba7dcSVincent Cheng 	int err;
13893a6ba7dcSVincent Cheng 	u8 dpll_mode;
13903a6ba7dcSVincent Cheng 
13913a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
13923a6ba7dcSVincent Cheng 			 &dpll_mode, sizeof(dpll_mode));
13933a6ba7dcSVincent Cheng 	if (err)
13943a6ba7dcSVincent Cheng 		return err;
13953a6ba7dcSVincent Cheng 
13963a6ba7dcSVincent Cheng 	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
13973a6ba7dcSVincent Cheng 
13983a6ba7dcSVincent Cheng 	dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
13993a6ba7dcSVincent Cheng 
14003a6ba7dcSVincent Cheng 	channel->pll_mode = pll_mode;
14013a6ba7dcSVincent Cheng 
14023a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
14033a6ba7dcSVincent Cheng 			  &dpll_mode, sizeof(dpll_mode));
14043a6ba7dcSVincent Cheng 	if (err)
14053a6ba7dcSVincent Cheng 		return err;
14063a6ba7dcSVincent Cheng 
14073a6ba7dcSVincent Cheng 	return 0;
14083a6ba7dcSVincent Cheng }
14093a6ba7dcSVincent Cheng 
14103a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */
14113a6ba7dcSVincent Cheng 
1412425d2b1cSVincent Cheng /**
1413425d2b1cSVincent Cheng  * @brief Maximum absolute value for write phase offset in picoseconds
1414425d2b1cSVincent Cheng  *
1415425d2b1cSVincent Cheng  * Destination signed register is 32-bit register in resolution of 50ps
1416425d2b1cSVincent Cheng  *
1417425d2b1cSVincent Cheng  * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1418425d2b1cSVincent Cheng  */
1419425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1420425d2b1cSVincent Cheng {
1421425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1422425d2b1cSVincent Cheng 	int err;
1423425d2b1cSVincent Cheng 	u8 i;
1424425d2b1cSVincent Cheng 	u8 buf[4] = {0};
1425425d2b1cSVincent Cheng 	s32 phase_50ps;
1426425d2b1cSVincent Cheng 	s64 offset_ps;
1427425d2b1cSVincent Cheng 
1428425d2b1cSVincent Cheng 	if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
1429425d2b1cSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
1430425d2b1cSVincent Cheng 		if (err)
1431425d2b1cSVincent Cheng 			return err;
1432425d2b1cSVincent Cheng 	}
1433425d2b1cSVincent Cheng 
1434425d2b1cSVincent Cheng 	offset_ps = (s64)delta_ns * 1000;
1435425d2b1cSVincent Cheng 
1436425d2b1cSVincent Cheng 	/*
1437425d2b1cSVincent Cheng 	 * Check for 32-bit signed max * 50:
1438425d2b1cSVincent Cheng 	 *
1439425d2b1cSVincent Cheng 	 * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
1440425d2b1cSVincent Cheng 	 */
1441425d2b1cSVincent Cheng 	if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
1442425d2b1cSVincent Cheng 		offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
1443425d2b1cSVincent Cheng 	else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
1444425d2b1cSVincent Cheng 		offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
1445425d2b1cSVincent Cheng 
14467260d1c8SMin Li 	phase_50ps = div_s64(offset_ps, 50);
1447425d2b1cSVincent Cheng 
1448425d2b1cSVincent Cheng 	for (i = 0; i < 4; i++) {
1449425d2b1cSVincent Cheng 		buf[i] = phase_50ps & 0xff;
1450425d2b1cSVincent Cheng 		phase_50ps >>= 8;
1451425d2b1cSVincent Cheng 	}
1452425d2b1cSVincent Cheng 
1453425d2b1cSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1454425d2b1cSVincent Cheng 			  buf, sizeof(buf));
1455425d2b1cSVincent Cheng 
1456425d2b1cSVincent Cheng 	return err;
1457425d2b1cSVincent Cheng }
1458425d2b1cSVincent Cheng 
14597ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
14603a6ba7dcSVincent Cheng {
14613a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
14623a6ba7dcSVincent Cheng 	u8 i;
14633a6ba7dcSVincent Cheng 	int err;
14643a6ba7dcSVincent Cheng 	u8 buf[6] = {0};
14653a6ba7dcSVincent Cheng 	s64 fcw;
14663a6ba7dcSVincent Cheng 
14673a6ba7dcSVincent Cheng 	if (channel->pll_mode  != PLL_MODE_WRITE_FREQUENCY) {
14683a6ba7dcSVincent Cheng 		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
14693a6ba7dcSVincent Cheng 		if (err)
14703a6ba7dcSVincent Cheng 			return err;
14713a6ba7dcSVincent Cheng 	}
14723a6ba7dcSVincent Cheng 
14733a6ba7dcSVincent Cheng 	/*
14743a6ba7dcSVincent Cheng 	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
14753a6ba7dcSVincent Cheng 	 *
14763a6ba7dcSVincent Cheng 	 * adjfreq:
14773a6ba7dcSVincent Cheng 	 *       ppb * 10^9
14783a6ba7dcSVincent Cheng 	 * FCW = ----------
14793a6ba7dcSVincent Cheng 	 *          111
14803a6ba7dcSVincent Cheng 	 *
14813a6ba7dcSVincent Cheng 	 * adjfine:
14823a6ba7dcSVincent Cheng 	 *       ppm_16 * 5^12
14833a6ba7dcSVincent Cheng 	 * FCW = -------------
14843a6ba7dcSVincent Cheng 	 *         111 * 2^4
14853a6ba7dcSVincent Cheng 	 */
14863a6ba7dcSVincent Cheng 
14873a6ba7dcSVincent Cheng 	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
14887ea5fda2SMin Li 	fcw = scaled_ppm * 244140625ULL;
14893a6ba7dcSVincent Cheng 
14907260d1c8SMin Li 	fcw = div_s64(fcw, 1776);
14913a6ba7dcSVincent Cheng 
14923a6ba7dcSVincent Cheng 	for (i = 0; i < 6; i++) {
14933a6ba7dcSVincent Cheng 		buf[i] = fcw & 0xff;
14943a6ba7dcSVincent Cheng 		fcw >>= 8;
14953a6ba7dcSVincent Cheng 	}
14963a6ba7dcSVincent Cheng 
14973a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
14983a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
14993a6ba7dcSVincent Cheng 
15003a6ba7dcSVincent Cheng 	return err;
15013a6ba7dcSVincent Cheng }
15023a6ba7dcSVincent Cheng 
15033a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
15043a6ba7dcSVincent Cheng {
1505*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15063a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15073a6ba7dcSVincent Cheng 	int err;
15083a6ba7dcSVincent Cheng 
15093a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
15103a6ba7dcSVincent Cheng 
15113a6ba7dcSVincent Cheng 	err = _idtcm_gettime(channel, ts);
15127ea5fda2SMin Li 	if (err)
15131c49d3e9SVincent Cheng 		dev_err(&idtcm->client->dev, "Failed at line %d in %s!",
15141c49d3e9SVincent Cheng 			__LINE__, __func__);
15157ea5fda2SMin Li 
15163a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
15173a6ba7dcSVincent Cheng 
15183a6ba7dcSVincent Cheng 	return err;
15193a6ba7dcSVincent Cheng }
15203a6ba7dcSVincent Cheng 
1521da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
15223a6ba7dcSVincent Cheng 				    const struct timespec64 *ts)
15233a6ba7dcSVincent Cheng {
1524*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15253a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15263a6ba7dcSVincent Cheng 	int err;
15273a6ba7dcSVincent Cheng 
15283a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
15293a6ba7dcSVincent Cheng 
1530da948233SMin Li 	err = _idtcm_settime_deprecated(channel, ts);
15317ea5fda2SMin Li 	if (err)
15327ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15331c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
15347ea5fda2SMin Li 
15357ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
15367ea5fda2SMin Li 
15377ea5fda2SMin Li 	return err;
15387ea5fda2SMin Li }
15397ea5fda2SMin Li 
1540da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp,
15417ea5fda2SMin Li 			 const struct timespec64 *ts)
15427ea5fda2SMin Li {
1543*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15447ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
15457ea5fda2SMin Li 	int err;
15467ea5fda2SMin Li 
15477ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
15487ea5fda2SMin Li 
1549da948233SMin Li 	err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
1550da948233SMin Li 	if (err)
1551da948233SMin Li 		dev_err(&idtcm->client->dev,
15521c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
1553da948233SMin Li 
1554da948233SMin Li 	mutex_unlock(&idtcm->reg_lock);
1555da948233SMin Li 
1556da948233SMin Li 	return err;
1557da948233SMin Li }
1558da948233SMin Li 
1559da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
1560da948233SMin Li {
1561*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1562da948233SMin Li 	struct idtcm *idtcm = channel->idtcm;
1563da948233SMin Li 	int err;
1564da948233SMin Li 
1565da948233SMin Li 	mutex_lock(&idtcm->reg_lock);
1566da948233SMin Li 
1567da948233SMin Li 	err = _idtcm_adjtime_deprecated(channel, delta);
15687ea5fda2SMin Li 	if (err)
15697ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
15701c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
15717ea5fda2SMin Li 
15723a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
15733a6ba7dcSVincent Cheng 
15743a6ba7dcSVincent Cheng 	return err;
15753a6ba7dcSVincent Cheng }
15763a6ba7dcSVincent Cheng 
15773a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
15783a6ba7dcSVincent Cheng {
1579*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
15803a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
15817ea5fda2SMin Li 	struct timespec64 ts;
15827ea5fda2SMin Li 	enum scsr_tod_write_type_sel type;
15837ea5fda2SMin Li 	int err;
15847ea5fda2SMin Li 
1585da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
15867ea5fda2SMin Li 		err = idtcm_do_phase_pull_in(channel, delta, 0);
15877ea5fda2SMin Li 		if (err)
15887ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
15891c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
15907ea5fda2SMin Li 		return err;
15917ea5fda2SMin Li 	}
15927ea5fda2SMin Li 
15937ea5fda2SMin Li 	if (delta >= 0) {
15947ea5fda2SMin Li 		ts = ns_to_timespec64(delta);
15957ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
15967ea5fda2SMin Li 	} else {
15977ea5fda2SMin Li 		ts = ns_to_timespec64(-delta);
15987ea5fda2SMin Li 		type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
15997ea5fda2SMin Li 	}
16007ea5fda2SMin Li 
16017ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
16027ea5fda2SMin Li 
1603da948233SMin Li 	err = _idtcm_settime(channel, &ts, type);
16047ea5fda2SMin Li 	if (err)
16057ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16061c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16077ea5fda2SMin Li 
16083a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
16093a6ba7dcSVincent Cheng 
16103a6ba7dcSVincent Cheng 	return err;
16113a6ba7dcSVincent Cheng }
16123a6ba7dcSVincent Cheng 
1613425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1614425d2b1cSVincent Cheng {
1615*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1616425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1617425d2b1cSVincent Cheng 	int err;
1618425d2b1cSVincent Cheng 
1619425d2b1cSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
1620425d2b1cSVincent Cheng 
1621425d2b1cSVincent Cheng 	err = _idtcm_adjphase(channel, delta);
16227ea5fda2SMin Li 	if (err)
16237ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16241c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16257ea5fda2SMin Li 
16267ea5fda2SMin Li 	mutex_unlock(&idtcm->reg_lock);
16277ea5fda2SMin Li 
16287ea5fda2SMin Li 	return err;
16297ea5fda2SMin Li }
16307ea5fda2SMin Li 
16317ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
16327ea5fda2SMin Li {
1633*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
16347ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
16357ea5fda2SMin Li 	int err;
16367ea5fda2SMin Li 
16377ea5fda2SMin Li 	mutex_lock(&idtcm->reg_lock);
16387ea5fda2SMin Li 
16397ea5fda2SMin Li 	err = _idtcm_adjfine(channel, scaled_ppm);
16407ea5fda2SMin Li 	if (err)
16417ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
16421c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
16437ea5fda2SMin Li 
1644425d2b1cSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
1645425d2b1cSVincent Cheng 
1646425d2b1cSVincent Cheng 	return err;
1647425d2b1cSVincent Cheng }
1648425d2b1cSVincent Cheng 
16493a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp,
16503a6ba7dcSVincent Cheng 			struct ptp_clock_request *rq, int on)
16513a6ba7dcSVincent Cheng {
16527ea5fda2SMin Li 	int err;
1653*fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
16543a6ba7dcSVincent Cheng 
16553a6ba7dcSVincent Cheng 	switch (rq->type) {
16563a6ba7dcSVincent Cheng 	case PTP_CLK_REQ_PEROUT:
16577ea5fda2SMin Li 		if (!on) {
16587ea5fda2SMin Li 			err = idtcm_perout_enable(channel, false, &rq->perout);
16597ea5fda2SMin Li 			if (err)
16607ea5fda2SMin Li 				dev_err(&channel->idtcm->client->dev,
16611c49d3e9SVincent Cheng 					"Failed at line %d in %s!",
16621c49d3e9SVincent Cheng 					__LINE__, __func__);
16637ea5fda2SMin Li 			return err;
16647ea5fda2SMin Li 		}
16653a6ba7dcSVincent Cheng 
16663a6ba7dcSVincent Cheng 		/* Only accept a 1-PPS aligned to the second. */
16673a6ba7dcSVincent Cheng 		if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
16683a6ba7dcSVincent Cheng 		    rq->perout.period.nsec)
16693a6ba7dcSVincent Cheng 			return -ERANGE;
16703a6ba7dcSVincent Cheng 
16717ea5fda2SMin Li 		err = idtcm_perout_enable(channel, true, &rq->perout);
16727ea5fda2SMin Li 		if (err)
16737ea5fda2SMin Li 			dev_err(&channel->idtcm->client->dev,
16741c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
16757ea5fda2SMin Li 		return err;
16763a6ba7dcSVincent Cheng 	default:
16773a6ba7dcSVincent Cheng 		break;
16783a6ba7dcSVincent Cheng 	}
16793a6ba7dcSVincent Cheng 
16803a6ba7dcSVincent Cheng 	return -EOPNOTSUPP;
16813a6ba7dcSVincent Cheng }
16823a6ba7dcSVincent Cheng 
16837ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm,
16847ea5fda2SMin Li 				u8 pll,
16857ea5fda2SMin Li 				u8 sync_src,
16867ea5fda2SMin Li 				u8 qn,
16877ea5fda2SMin Li 				u8 qn_plus_1)
16887ea5fda2SMin Li {
16897ea5fda2SMin Li 	int err;
16907ea5fda2SMin Li 	u8 val;
16917ea5fda2SMin Li 	u16 dpll;
16927ea5fda2SMin Li 	u16 out0 = 0, out1 = 0;
16937ea5fda2SMin Li 
16947ea5fda2SMin Li 	if ((qn == 0) && (qn_plus_1 == 0))
16957ea5fda2SMin Li 		return 0;
16967ea5fda2SMin Li 
16977ea5fda2SMin Li 	switch (pll) {
16987ea5fda2SMin Li 	case 0:
16997ea5fda2SMin Li 		dpll = DPLL_0;
17007ea5fda2SMin Li 		if (qn)
17017ea5fda2SMin Li 			out0 = OUTPUT_0;
17027ea5fda2SMin Li 		if (qn_plus_1)
17037ea5fda2SMin Li 			out1 = OUTPUT_1;
17047ea5fda2SMin Li 		break;
17057ea5fda2SMin Li 	case 1:
17067ea5fda2SMin Li 		dpll = DPLL_1;
17077ea5fda2SMin Li 		if (qn)
17087ea5fda2SMin Li 			out0 = OUTPUT_2;
17097ea5fda2SMin Li 		if (qn_plus_1)
17107ea5fda2SMin Li 			out1 = OUTPUT_3;
17117ea5fda2SMin Li 		break;
17127ea5fda2SMin Li 	case 2:
17137ea5fda2SMin Li 		dpll = DPLL_2;
17147ea5fda2SMin Li 		if (qn)
17157ea5fda2SMin Li 			out0 = OUTPUT_4;
17167ea5fda2SMin Li 		if (qn_plus_1)
17177ea5fda2SMin Li 			out1 = OUTPUT_5;
17187ea5fda2SMin Li 		break;
17197ea5fda2SMin Li 	case 3:
17207ea5fda2SMin Li 		dpll = DPLL_3;
17217ea5fda2SMin Li 		if (qn)
17227ea5fda2SMin Li 			out0 = OUTPUT_6;
17237ea5fda2SMin Li 		if (qn_plus_1)
17247ea5fda2SMin Li 			out1 = OUTPUT_7;
17257ea5fda2SMin Li 		break;
17267ea5fda2SMin Li 	case 4:
17277ea5fda2SMin Li 		dpll = DPLL_4;
17287ea5fda2SMin Li 		if (qn)
17297ea5fda2SMin Li 			out0 = OUTPUT_8;
17307ea5fda2SMin Li 		break;
17317ea5fda2SMin Li 	case 5:
17327ea5fda2SMin Li 		dpll = DPLL_5;
17337ea5fda2SMin Li 		if (qn)
17347ea5fda2SMin Li 			out0 = OUTPUT_9;
17357ea5fda2SMin Li 		if (qn_plus_1)
17367ea5fda2SMin Li 			out1 = OUTPUT_8;
17377ea5fda2SMin Li 		break;
17387ea5fda2SMin Li 	case 6:
17397ea5fda2SMin Li 		dpll = DPLL_6;
17407ea5fda2SMin Li 		if (qn)
17417ea5fda2SMin Li 			out0 = OUTPUT_10;
17427ea5fda2SMin Li 		if (qn_plus_1)
17437ea5fda2SMin Li 			out1 = OUTPUT_11;
17447ea5fda2SMin Li 		break;
17457ea5fda2SMin Li 	case 7:
17467ea5fda2SMin Li 		dpll = DPLL_7;
17477ea5fda2SMin Li 		if (qn)
17487ea5fda2SMin Li 			out0 = OUTPUT_11;
17497ea5fda2SMin Li 		break;
17507ea5fda2SMin Li 	default:
17517ea5fda2SMin Li 		return -EINVAL;
17527ea5fda2SMin Li 	}
17537ea5fda2SMin Li 
17547ea5fda2SMin Li 	/*
17557ea5fda2SMin Li 	 * Enable OUTPUT OUT_SYNC.
17567ea5fda2SMin Li 	 */
17577ea5fda2SMin Li 	if (out0) {
17587ea5fda2SMin Li 		err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17597ea5fda2SMin Li 		if (err)
17607ea5fda2SMin Li 			return err;
17617ea5fda2SMin Li 
17627ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17637ea5fda2SMin Li 
17647ea5fda2SMin Li 		err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
17657ea5fda2SMin Li 		if (err)
17667ea5fda2SMin Li 			return err;
17677ea5fda2SMin Li 	}
17687ea5fda2SMin Li 
17697ea5fda2SMin Li 	if (out1) {
17707ea5fda2SMin Li 		err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17717ea5fda2SMin Li 		if (err)
17727ea5fda2SMin Li 			return err;
17737ea5fda2SMin Li 
17747ea5fda2SMin Li 		val &= ~OUT_SYNC_DISABLE;
17757ea5fda2SMin Li 
17767ea5fda2SMin Li 		err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
17777ea5fda2SMin Li 		if (err)
17787ea5fda2SMin Li 			return err;
17797ea5fda2SMin Li 	}
17807ea5fda2SMin Li 
17817ea5fda2SMin Li 	/* enable dpll sync tod pps, must be set before dpll_mode */
17827ea5fda2SMin Li 	err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
17837ea5fda2SMin Li 	if (err)
17847ea5fda2SMin Li 		return err;
17857ea5fda2SMin Li 
17867ea5fda2SMin Li 	val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
17877ea5fda2SMin Li 	val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
17887ea5fda2SMin Li 	val |= TOD_SYNC_EN;
17897ea5fda2SMin Li 
17907ea5fda2SMin Li 	return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
17917ea5fda2SMin Li }
17927ea5fda2SMin Li 
17937ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel)
17947ea5fda2SMin Li {
17957ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
17967ea5fda2SMin Li 	u8 pll;
17977ea5fda2SMin Li 	u8 sync_src;
17987ea5fda2SMin Li 	u8 qn;
17997ea5fda2SMin Li 	u8 qn_plus_1;
18007ea5fda2SMin Li 	u8 cfg;
18017ea5fda2SMin Li 	int err = 0;
18027ea5fda2SMin Li 	u16 output_mask = channel->output_mask;
18037ea5fda2SMin Li 	u8 out8_mux = 0;
18047ea5fda2SMin Li 	u8 out11_mux = 0;
18057ea5fda2SMin Li 	u8 temp;
18067ea5fda2SMin Li 
18077ea5fda2SMin Li 	/*
18087ea5fda2SMin Li 	 * set tod_out_sync_enable to 0.
18097ea5fda2SMin Li 	 */
18107ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18117ea5fda2SMin Li 	if (err)
18127ea5fda2SMin Li 		return err;
18137ea5fda2SMin Li 
18147ea5fda2SMin Li 	cfg &= ~TOD_OUT_SYNC_ENABLE;
18157ea5fda2SMin Li 
18167ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18177ea5fda2SMin Li 	if (err)
18187ea5fda2SMin Li 		return err;
18197ea5fda2SMin Li 
18207ea5fda2SMin Li 	switch (channel->tod_n) {
18217ea5fda2SMin Li 	case TOD_0:
18227ea5fda2SMin Li 		sync_src = 0;
18237ea5fda2SMin Li 		break;
18247ea5fda2SMin Li 	case TOD_1:
18257ea5fda2SMin Li 		sync_src = 1;
18267ea5fda2SMin Li 		break;
18277ea5fda2SMin Li 	case TOD_2:
18287ea5fda2SMin Li 		sync_src = 2;
18297ea5fda2SMin Li 		break;
18307ea5fda2SMin Li 	case TOD_3:
18317ea5fda2SMin Li 		sync_src = 3;
18327ea5fda2SMin Li 		break;
18337ea5fda2SMin Li 	default:
18347ea5fda2SMin Li 		return -EINVAL;
18357ea5fda2SMin Li 	}
18367ea5fda2SMin Li 
1837*fcfd3757SVincent Cheng 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp));
18387ea5fda2SMin Li 	if (err)
18397ea5fda2SMin Li 		return err;
18407ea5fda2SMin Li 
18417ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18427ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18437ea5fda2SMin Li 		out8_mux = 1;
18447ea5fda2SMin Li 
1845*fcfd3757SVincent Cheng 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp));
18467ea5fda2SMin Li 	if (err)
18477ea5fda2SMin Li 		return err;
18487ea5fda2SMin Li 
18497ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
18507ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
18517ea5fda2SMin Li 		out11_mux = 1;
18527ea5fda2SMin Li 
18537ea5fda2SMin Li 	for (pll = 0; pll < 8; pll++) {
18547ea5fda2SMin Li 		qn = 0;
18557ea5fda2SMin Li 		qn_plus_1 = 0;
18567ea5fda2SMin Li 
18577ea5fda2SMin Li 		if (pll < 4) {
18587ea5fda2SMin Li 			/* First 4 pll has 2 outputs */
18597ea5fda2SMin Li 			qn = output_mask & 0x1;
18607ea5fda2SMin Li 			output_mask = output_mask >> 1;
18617ea5fda2SMin Li 			qn_plus_1 = output_mask & 0x1;
18627ea5fda2SMin Li 			output_mask = output_mask >> 1;
18637ea5fda2SMin Li 		} else if (pll == 4) {
18647ea5fda2SMin Li 			if (out8_mux == 0) {
18657ea5fda2SMin Li 				qn = output_mask & 0x1;
18667ea5fda2SMin Li 				output_mask = output_mask >> 1;
18677ea5fda2SMin Li 			}
18687ea5fda2SMin Li 		} else if (pll == 5) {
18697ea5fda2SMin Li 			if (out8_mux) {
18707ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
18717ea5fda2SMin Li 				output_mask = output_mask >> 1;
18727ea5fda2SMin Li 			}
18737ea5fda2SMin Li 			qn = output_mask & 0x1;
18747ea5fda2SMin Li 			output_mask = output_mask >> 1;
18757ea5fda2SMin Li 		} else if (pll == 6) {
18767ea5fda2SMin Li 			qn = output_mask & 0x1;
18777ea5fda2SMin Li 			output_mask = output_mask >> 1;
18787ea5fda2SMin Li 			if (out11_mux) {
18797ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
18807ea5fda2SMin Li 				output_mask = output_mask >> 1;
18817ea5fda2SMin Li 			}
18827ea5fda2SMin Li 		} else if (pll == 7) {
18837ea5fda2SMin Li 			if (out11_mux == 0) {
18847ea5fda2SMin Li 				qn = output_mask & 0x1;
18857ea5fda2SMin Li 				output_mask = output_mask >> 1;
18867ea5fda2SMin Li 			}
18877ea5fda2SMin Li 		}
18887ea5fda2SMin Li 
18897ea5fda2SMin Li 		if ((qn != 0) || (qn_plus_1 != 0))
18907ea5fda2SMin Li 			err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
18917ea5fda2SMin Li 					       qn_plus_1);
18927ea5fda2SMin Li 		if (err)
18937ea5fda2SMin Li 			return err;
18947ea5fda2SMin Li 	}
18957ea5fda2SMin Li 
18967ea5fda2SMin Li 	return err;
18977ea5fda2SMin Li }
18987ea5fda2SMin Li 
18993a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel)
19003a6ba7dcSVincent Cheng {
19013a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
19023a6ba7dcSVincent Cheng 	struct timespec64 ts = {0, 0};
19033a6ba7dcSVincent Cheng 	u8 cfg;
19043a6ba7dcSVincent Cheng 	int err;
19053a6ba7dcSVincent Cheng 
19063a6ba7dcSVincent Cheng 	/*
19073a6ba7dcSVincent Cheng 	 * Start the TOD clock ticking.
19083a6ba7dcSVincent Cheng 	 */
19093a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19103a6ba7dcSVincent Cheng 	if (err)
19113a6ba7dcSVincent Cheng 		return err;
19123a6ba7dcSVincent Cheng 
19133a6ba7dcSVincent Cheng 	cfg |= TOD_ENABLE;
19143a6ba7dcSVincent Cheng 
19153a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
19163a6ba7dcSVincent Cheng 	if (err)
19173a6ba7dcSVincent Cheng 		return err;
19183a6ba7dcSVincent Cheng 
1919da948233SMin Li 	if (idtcm->deprecated)
1920da948233SMin Li 		return _idtcm_settime_deprecated(channel, &ts);
1921da948233SMin Li 	else
1922da948233SMin Li 		return _idtcm_settime(channel, &ts,
1923da948233SMin Li 				      SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
19243a6ba7dcSVincent Cheng }
19253a6ba7dcSVincent Cheng 
1926da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm)
19273a6ba7dcSVincent Cheng {
19283a6ba7dcSVincent Cheng 	u8 major;
19293a6ba7dcSVincent Cheng 	u8 minor;
19303a6ba7dcSVincent Cheng 	u8 hotfix;
19313a6ba7dcSVincent Cheng 	u16 product_id;
19323a6ba7dcSVincent Cheng 	u8 hw_rev_id;
19331ece2fbeSVincent Cheng 	u8 config_select;
19343a6ba7dcSVincent Cheng 
19353a6ba7dcSVincent Cheng 	idtcm_read_major_release(idtcm, &major);
19363a6ba7dcSVincent Cheng 	idtcm_read_minor_release(idtcm, &minor);
19373a6ba7dcSVincent Cheng 	idtcm_read_hotfix_release(idtcm, &hotfix);
19383a6ba7dcSVincent Cheng 
19393a6ba7dcSVincent Cheng 	idtcm_read_product_id(idtcm, &product_id);
19403a6ba7dcSVincent Cheng 	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
19413a6ba7dcSVincent Cheng 
19421ece2fbeSVincent Cheng 	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
19431ece2fbeSVincent Cheng 
19447ea5fda2SMin Li 	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
19457ea5fda2SMin Li 		 major, minor, hotfix);
19467ea5fda2SMin Li 
1947da948233SMin Li 	if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
1948da948233SMin Li 		idtcm->deprecated = 0;
1949da948233SMin Li 	else
1950da948233SMin Li 		idtcm->deprecated = 1;
1951da948233SMin Li 
19521c49d3e9SVincent Cheng 	dev_info(&idtcm->client->dev,
19531c49d3e9SVincent Cheng 		 "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d",
19541c49d3e9SVincent Cheng 		 major, minor, hotfix,
19551ece2fbeSVincent Cheng 		 product_id, hw_rev_id, config_select);
19563a6ba7dcSVincent Cheng }
19573a6ba7dcSVincent Cheng 
19586485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = {
19593a6ba7dcSVincent Cheng 	.owner		= THIS_MODULE,
19603a6ba7dcSVincent Cheng 	.max_adj	= 244000,
19617ea5fda2SMin Li 	.n_per_out	= 12,
1962425d2b1cSVincent Cheng 	.adjphase	= &idtcm_adjphase,
19637ea5fda2SMin Li 	.adjfine	= &idtcm_adjfine,
19643a6ba7dcSVincent Cheng 	.adjtime	= &idtcm_adjtime,
19653a6ba7dcSVincent Cheng 	.gettime64	= &idtcm_gettime,
19663a6ba7dcSVincent Cheng 	.settime64	= &idtcm_settime,
19673a6ba7dcSVincent Cheng 	.enable		= &idtcm_enable,
19683a6ba7dcSVincent Cheng };
19693a6ba7dcSVincent Cheng 
1970da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = {
1971da948233SMin Li 	.owner		= THIS_MODULE,
1972da948233SMin Li 	.max_adj	= 244000,
1973da948233SMin Li 	.n_per_out	= 12,
1974da948233SMin Li 	.adjphase	= &idtcm_adjphase,
1975da948233SMin Li 	.adjfine	= &idtcm_adjfine,
1976da948233SMin Li 	.adjtime	= &idtcm_adjtime_deprecated,
1977da948233SMin Li 	.gettime64	= &idtcm_gettime,
1978da948233SMin Li 	.settime64	= &idtcm_settime_deprecated,
1979da948233SMin Li 	.enable		= &idtcm_enable,
1980da948233SMin Li };
1981da948233SMin Li 
19827ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel)
19833a6ba7dcSVincent Cheng {
19847ea5fda2SMin Li 	int err = 0;
19853a6ba7dcSVincent Cheng 
19867ea5fda2SMin Li 	switch (channel->pll) {
19873a6ba7dcSVincent Cheng 	case 0:
19883a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_0;
19893a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_0;
19903a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_0;
19913a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_0;
19923a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_0;
19933a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
19943a6ba7dcSVincent Cheng 		break;
19953a6ba7dcSVincent Cheng 	case 1:
19963a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_1;
19973a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_1;
19983a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_1;
19993a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_1;
20003a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_1;
20013a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
20023a6ba7dcSVincent Cheng 		break;
20033a6ba7dcSVincent Cheng 	case 2:
20043a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_2;
20053a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_2;
20063a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_2;
20073a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_2;
20083a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_2;
20093a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
20103a6ba7dcSVincent Cheng 		break;
20113a6ba7dcSVincent Cheng 	case 3:
20123a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_3;
20133a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_3;
20143a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_3;
20153a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_3;
20163a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_3;
20173a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
20183a6ba7dcSVincent Cheng 		break;
20197ea5fda2SMin Li 	case 4:
20207ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_4;
20217ea5fda2SMin Li 		channel->dpll_n = DPLL_4;
20227ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_4;
20237ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_4;
20247ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_4;
20257ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
20267ea5fda2SMin Li 		break;
20277ea5fda2SMin Li 	case 5:
20287ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_5;
20297ea5fda2SMin Li 		channel->dpll_n = DPLL_5;
20307ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_5;
20317ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_5;
20327ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_5;
20337ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
20347ea5fda2SMin Li 		break;
20357ea5fda2SMin Li 	case 6:
20367ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_6;
20377ea5fda2SMin Li 		channel->dpll_n = DPLL_6;
20387ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_6;
20397ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_6;
20407ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_6;
20417ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
20427ea5fda2SMin Li 		break;
20437ea5fda2SMin Li 	case 7:
20447ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_7;
20457ea5fda2SMin Li 		channel->dpll_n = DPLL_7;
20467ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_7;
20477ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_7;
20487ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_7;
20497ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
20507ea5fda2SMin Li 		break;
20517ea5fda2SMin Li 	default:
20527ea5fda2SMin Li 		err = -EINVAL;
20537ea5fda2SMin Li 	}
20547ea5fda2SMin Li 
20557ea5fda2SMin Li 	return err;
20567ea5fda2SMin Li }
20577ea5fda2SMin Li 
20587ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
20597ea5fda2SMin Li {
20607ea5fda2SMin Li 	struct idtcm_channel *channel;
20617ea5fda2SMin Li 	int err;
20627ea5fda2SMin Li 
20637ea5fda2SMin Li 	if (!(index < MAX_TOD))
20647ea5fda2SMin Li 		return -EINVAL;
20657ea5fda2SMin Li 
20667ea5fda2SMin Li 	channel = &idtcm->channel[index];
20677ea5fda2SMin Li 
20687ea5fda2SMin Li 	/* Set pll addresses */
20697ea5fda2SMin Li 	err = configure_channel_pll(channel);
20707ea5fda2SMin Li 	if (err)
20717ea5fda2SMin Li 		return err;
20727ea5fda2SMin Li 
20737ea5fda2SMin Li 	/* Set tod addresses */
20747ea5fda2SMin Li 	switch (index) {
20757ea5fda2SMin Li 	case 0:
20767ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_0;
20777ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_0;
20787ea5fda2SMin Li 		channel->tod_n = TOD_0;
20797ea5fda2SMin Li 		break;
20807ea5fda2SMin Li 	case 1:
20817ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_1;
20827ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_1;
20837ea5fda2SMin Li 		channel->tod_n = TOD_1;
20847ea5fda2SMin Li 		break;
20857ea5fda2SMin Li 	case 2:
20867ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_2;
20877ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_2;
20887ea5fda2SMin Li 		channel->tod_n = TOD_2;
20897ea5fda2SMin Li 		break;
20907ea5fda2SMin Li 	case 3:
20917ea5fda2SMin Li 		channel->tod_read_primary = TOD_READ_PRIMARY_3;
20927ea5fda2SMin Li 		channel->tod_write = TOD_WRITE_3;
20937ea5fda2SMin Li 		channel->tod_n = TOD_3;
20947ea5fda2SMin Li 		break;
20953a6ba7dcSVincent Cheng 	default:
20963a6ba7dcSVincent Cheng 		return -EINVAL;
20973a6ba7dcSVincent Cheng 	}
20983a6ba7dcSVincent Cheng 
20993a6ba7dcSVincent Cheng 	channel->idtcm = idtcm;
21003a6ba7dcSVincent Cheng 
2101da948233SMin Li 	if (idtcm->deprecated)
2102da948233SMin Li 		channel->caps = idtcm_caps_deprecated;
21037ea5fda2SMin Li 	else
21043a6ba7dcSVincent Cheng 		channel->caps = idtcm_caps;
21057ea5fda2SMin Li 
21063a6ba7dcSVincent Cheng 	snprintf(channel->caps.name, sizeof(channel->caps.name),
21077ea5fda2SMin Li 		 "IDT CM TOD%u", index);
21087ea5fda2SMin Li 
2109da948233SMin Li 	if (!idtcm->deprecated) {
21107ea5fda2SMin Li 		err = idtcm_enable_tod_sync(channel);
21117ea5fda2SMin Li 		if (err) {
21127ea5fda2SMin Li 			dev_err(&idtcm->client->dev,
21131c49d3e9SVincent Cheng 				"Failed at line %d in %s!", __LINE__, __func__);
21147ea5fda2SMin Li 			return err;
21157ea5fda2SMin Li 		}
21167ea5fda2SMin Li 	}
21173a6ba7dcSVincent Cheng 
21187260d1c8SMin Li 	/* Sync pll mode with hardware */
21197260d1c8SMin Li 	err = idtcm_get_pll_mode(channel, &channel->pll_mode);
21207ea5fda2SMin Li 	if (err) {
21217ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21221c49d3e9SVincent Cheng 			"Error: %s - Unable to read pll mode", __func__);
21233a6ba7dcSVincent Cheng 		return err;
21247ea5fda2SMin Li 	}
21253a6ba7dcSVincent Cheng 
21263a6ba7dcSVincent Cheng 	err = idtcm_enable_tod(channel);
21277ea5fda2SMin Li 	if (err) {
21287ea5fda2SMin Li 		dev_err(&idtcm->client->dev,
21291c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
21303a6ba7dcSVincent Cheng 		return err;
21317ea5fda2SMin Li 	}
21323a6ba7dcSVincent Cheng 
21333a6ba7dcSVincent Cheng 	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
21343a6ba7dcSVincent Cheng 
21353a6ba7dcSVincent Cheng 	if (IS_ERR(channel->ptp_clock)) {
21363a6ba7dcSVincent Cheng 		err = PTR_ERR(channel->ptp_clock);
21373a6ba7dcSVincent Cheng 		channel->ptp_clock = NULL;
21383a6ba7dcSVincent Cheng 		return err;
21393a6ba7dcSVincent Cheng 	}
21403a6ba7dcSVincent Cheng 
21413a6ba7dcSVincent Cheng 	if (!channel->ptp_clock)
21423a6ba7dcSVincent Cheng 		return -ENOTSUPP;
21433a6ba7dcSVincent Cheng 
21441c49d3e9SVincent Cheng 	dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d",
21453a6ba7dcSVincent Cheng 		 index, channel->ptp_clock->index);
21463a6ba7dcSVincent Cheng 
21473a6ba7dcSVincent Cheng 	return 0;
21483a6ba7dcSVincent Cheng }
21493a6ba7dcSVincent Cheng 
21503a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm)
21513a6ba7dcSVincent Cheng {
21523a6ba7dcSVincent Cheng 	u8 i;
21533a6ba7dcSVincent Cheng 	struct idtcm_channel *channel;
21543a6ba7dcSVincent Cheng 
21557ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
21563a6ba7dcSVincent Cheng 		channel = &idtcm->channel[i];
21573a6ba7dcSVincent Cheng 
21583a6ba7dcSVincent Cheng 		if (channel->ptp_clock)
21593a6ba7dcSVincent Cheng 			ptp_clock_unregister(channel->ptp_clock);
21603a6ba7dcSVincent Cheng 	}
21613a6ba7dcSVincent Cheng }
21623a6ba7dcSVincent Cheng 
21633a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm)
21643a6ba7dcSVincent Cheng {
21657ea5fda2SMin Li 	idtcm->tod_mask = DEFAULT_TOD_MASK;
21667ea5fda2SMin Li 
21677ea5fda2SMin Li 	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
21687ea5fda2SMin Li 	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
21697ea5fda2SMin Li 	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
21707ea5fda2SMin Li 	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
21713a6ba7dcSVincent Cheng 
21723a6ba7dcSVincent Cheng 	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
21733a6ba7dcSVincent Cheng 	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
21743a6ba7dcSVincent Cheng 	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
21753a6ba7dcSVincent Cheng 	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
21763a6ba7dcSVincent Cheng }
21773a6ba7dcSVincent Cheng 
21783a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client,
21793a6ba7dcSVincent Cheng 		       const struct i2c_device_id *id)
21803a6ba7dcSVincent Cheng {
21813a6ba7dcSVincent Cheng 	struct idtcm *idtcm;
21823a6ba7dcSVincent Cheng 	int err;
21833a6ba7dcSVincent Cheng 	u8 i;
21843a6ba7dcSVincent Cheng 
21853a6ba7dcSVincent Cheng 	/* Unused for now */
21863a6ba7dcSVincent Cheng 	(void)id;
21873a6ba7dcSVincent Cheng 
21883a6ba7dcSVincent Cheng 	idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
21893a6ba7dcSVincent Cheng 
21903a6ba7dcSVincent Cheng 	if (!idtcm)
21913a6ba7dcSVincent Cheng 		return -ENOMEM;
21923a6ba7dcSVincent Cheng 
21933a6ba7dcSVincent Cheng 	idtcm->client = client;
21943a6ba7dcSVincent Cheng 	idtcm->page_offset = 0xff;
21953a6ba7dcSVincent Cheng 	idtcm->calculate_overhead_flag = 0;
21963a6ba7dcSVincent Cheng 
21973a6ba7dcSVincent Cheng 	set_default_masks(idtcm);
21983a6ba7dcSVincent Cheng 
21993a6ba7dcSVincent Cheng 	mutex_init(&idtcm->reg_lock);
22003a6ba7dcSVincent Cheng 	mutex_lock(&idtcm->reg_lock);
22013a6ba7dcSVincent Cheng 
2202da948233SMin Li 	idtcm_set_version_info(idtcm);
22033a6ba7dcSVincent Cheng 
22043a6ba7dcSVincent Cheng 	err = idtcm_load_firmware(idtcm, &client->dev);
22053a6ba7dcSVincent Cheng 	if (err)
2206*fcfd3757SVincent Cheng 		dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err);
22073a6ba7dcSVincent Cheng 
2208797d3186SVincent Cheng 	wait_for_chip_ready(idtcm);
2209251f4fe2SMin Li 
22107ea5fda2SMin Li 	if (idtcm->tod_mask) {
22117ea5fda2SMin Li 		for (i = 0; i < MAX_TOD; i++) {
22127ea5fda2SMin Li 			if (idtcm->tod_mask & (1 << i)) {
22133a6ba7dcSVincent Cheng 				err = idtcm_enable_channel(idtcm, i);
22147ea5fda2SMin Li 				if (err) {
22157ea5fda2SMin Li 					dev_err(&idtcm->client->dev,
22161c49d3e9SVincent Cheng 						"idtcm_enable_channel %d failed!", i);
22173a6ba7dcSVincent Cheng 					break;
22183a6ba7dcSVincent Cheng 				}
22193a6ba7dcSVincent Cheng 			}
22207ea5fda2SMin Li 		}
22213a6ba7dcSVincent Cheng 	} else {
22223a6ba7dcSVincent Cheng 		dev_err(&idtcm->client->dev,
22231c49d3e9SVincent Cheng 			"no PLLs flagged as PHCs, nothing to do");
22243a6ba7dcSVincent Cheng 		err = -ENODEV;
22253a6ba7dcSVincent Cheng 	}
22263a6ba7dcSVincent Cheng 
22273a6ba7dcSVincent Cheng 	mutex_unlock(&idtcm->reg_lock);
22283a6ba7dcSVincent Cheng 
22293a6ba7dcSVincent Cheng 	if (err) {
22303a6ba7dcSVincent Cheng 		ptp_clock_unregister_all(idtcm);
22313a6ba7dcSVincent Cheng 		return err;
22323a6ba7dcSVincent Cheng 	}
22333a6ba7dcSVincent Cheng 
22343a6ba7dcSVincent Cheng 	i2c_set_clientdata(client, idtcm);
22353a6ba7dcSVincent Cheng 
22363a6ba7dcSVincent Cheng 	return 0;
22373a6ba7dcSVincent Cheng }
22383a6ba7dcSVincent Cheng 
22393a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client)
22403a6ba7dcSVincent Cheng {
22413a6ba7dcSVincent Cheng 	struct idtcm *idtcm = i2c_get_clientdata(client);
22423a6ba7dcSVincent Cheng 
22433a6ba7dcSVincent Cheng 	ptp_clock_unregister_all(idtcm);
22443a6ba7dcSVincent Cheng 
22453a6ba7dcSVincent Cheng 	mutex_destroy(&idtcm->reg_lock);
22463a6ba7dcSVincent Cheng 
22473a6ba7dcSVincent Cheng 	return 0;
22483a6ba7dcSVincent Cheng }
22493a6ba7dcSVincent Cheng 
22503a6ba7dcSVincent Cheng #ifdef CONFIG_OF
22513a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = {
22523a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34000" },
22533a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34001" },
22543a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34002" },
22553a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34003" },
22563a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34004" },
22573a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34005" },
22583a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34006" },
22593a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34007" },
22603a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34008" },
22613a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34009" },
22623a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34010" },
22633a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34011" },
22643a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34012" },
22653a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34013" },
22663a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34014" },
22673a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34015" },
22683a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34016" },
22693a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34017" },
22703a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34018" },
22713a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34019" },
22723a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34040" },
22733a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34041" },
22743a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34042" },
22753a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34043" },
22763a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34044" },
22773a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34045" },
22783a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34046" },
22793a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34047" },
22803a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34048" },
22813a6ba7dcSVincent Cheng 	{ .compatible = "idt,8a34049" },
22823a6ba7dcSVincent Cheng 	{},
22833a6ba7dcSVincent Cheng };
22843a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id);
22853a6ba7dcSVincent Cheng #endif
22863a6ba7dcSVincent Cheng 
22873a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = {
22883a6ba7dcSVincent Cheng 	{ "8a34000" },
22893a6ba7dcSVincent Cheng 	{ "8a34001" },
22903a6ba7dcSVincent Cheng 	{ "8a34002" },
22913a6ba7dcSVincent Cheng 	{ "8a34003" },
22923a6ba7dcSVincent Cheng 	{ "8a34004" },
22933a6ba7dcSVincent Cheng 	{ "8a34005" },
22943a6ba7dcSVincent Cheng 	{ "8a34006" },
22953a6ba7dcSVincent Cheng 	{ "8a34007" },
22963a6ba7dcSVincent Cheng 	{ "8a34008" },
22973a6ba7dcSVincent Cheng 	{ "8a34009" },
22983a6ba7dcSVincent Cheng 	{ "8a34010" },
22993a6ba7dcSVincent Cheng 	{ "8a34011" },
23003a6ba7dcSVincent Cheng 	{ "8a34012" },
23013a6ba7dcSVincent Cheng 	{ "8a34013" },
23023a6ba7dcSVincent Cheng 	{ "8a34014" },
23033a6ba7dcSVincent Cheng 	{ "8a34015" },
23043a6ba7dcSVincent Cheng 	{ "8a34016" },
23053a6ba7dcSVincent Cheng 	{ "8a34017" },
23063a6ba7dcSVincent Cheng 	{ "8a34018" },
23073a6ba7dcSVincent Cheng 	{ "8a34019" },
23083a6ba7dcSVincent Cheng 	{ "8a34040" },
23093a6ba7dcSVincent Cheng 	{ "8a34041" },
23103a6ba7dcSVincent Cheng 	{ "8a34042" },
23113a6ba7dcSVincent Cheng 	{ "8a34043" },
23123a6ba7dcSVincent Cheng 	{ "8a34044" },
23133a6ba7dcSVincent Cheng 	{ "8a34045" },
23143a6ba7dcSVincent Cheng 	{ "8a34046" },
23153a6ba7dcSVincent Cheng 	{ "8a34047" },
23163a6ba7dcSVincent Cheng 	{ "8a34048" },
23173a6ba7dcSVincent Cheng 	{ "8a34049" },
23183a6ba7dcSVincent Cheng 	{},
23193a6ba7dcSVincent Cheng };
23203a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
23213a6ba7dcSVincent Cheng 
23223a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = {
23233a6ba7dcSVincent Cheng 	.driver = {
23243a6ba7dcSVincent Cheng 		.of_match_table	= of_match_ptr(idtcm_dt_id),
23253a6ba7dcSVincent Cheng 		.name		= "idtcm",
23263a6ba7dcSVincent Cheng 	},
23273a6ba7dcSVincent Cheng 	.probe		= idtcm_probe,
23283a6ba7dcSVincent Cheng 	.remove		= idtcm_remove,
23293a6ba7dcSVincent Cheng 	.id_table	= idtcm_i2c_id,
23303a6ba7dcSVincent Cheng };
23313a6ba7dcSVincent Cheng 
23323a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver);
2333