xref: /openbmc/linux/drivers/ptp/ptp_clockmatrix.c (revision c066e74f)
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>
9930dfa56SMin Li #include <linux/platform_device.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>
17930dfa56SMin Li #include <linux/of.h>
18930dfa56SMin Li #include <linux/mfd/rsmu.h>
19930dfa56SMin Li #include <linux/mfd/idt8a340_reg.h>
20930dfa56SMin Li #include <asm/unaligned.h>
213a6ba7dcSVincent Cheng 
223a6ba7dcSVincent Cheng #include "ptp_private.h"
233a6ba7dcSVincent Cheng #include "ptp_clockmatrix.h"
243a6ba7dcSVincent Cheng 
253a6ba7dcSVincent Cheng MODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family");
263a6ba7dcSVincent Cheng MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
273a6ba7dcSVincent Cheng MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
283a6ba7dcSVincent Cheng MODULE_VERSION("1.0");
293a6ba7dcSVincent Cheng MODULE_LICENSE("GPL");
303a6ba7dcSVincent Cheng 
317ea5fda2SMin Li /*
327ea5fda2SMin Li  * The name of the firmware file to be loaded
337ea5fda2SMin Li  * over-rides any automatic selection
347ea5fda2SMin Li  */
357ea5fda2SMin Li static char *firmware;
367ea5fda2SMin Li module_param(firmware, charp, 0);
377ea5fda2SMin Li 
383a6ba7dcSVincent Cheng #define SETTIME_CORRECTION (0)
39930dfa56SMin Li #define EXTTS_PERIOD_MS (95)
403a6ba7dcSVincent Cheng 
41da9facf1SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm);
42da9facf1SMin Li 
idtcm_read(struct idtcm * idtcm,u16 module,u16 regaddr,u8 * buf,u16 count)43930dfa56SMin Li static inline int idtcm_read(struct idtcm *idtcm,
44930dfa56SMin Li 			     u16 module,
45930dfa56SMin Li 			     u16 regaddr,
46930dfa56SMin Li 			     u8 *buf,
47930dfa56SMin Li 			     u16 count)
48930dfa56SMin Li {
49930dfa56SMin Li 	return regmap_bulk_read(idtcm->regmap, module + regaddr, buf, count);
50930dfa56SMin Li }
51930dfa56SMin Li 
idtcm_write(struct idtcm * idtcm,u16 module,u16 regaddr,u8 * buf,u16 count)52930dfa56SMin Li static inline int idtcm_write(struct idtcm *idtcm,
53930dfa56SMin Li 			      u16 module,
54930dfa56SMin Li 			      u16 regaddr,
55930dfa56SMin Li 			      u8 *buf,
56930dfa56SMin Li 			      u16 count)
57930dfa56SMin Li {
58930dfa56SMin Li 	return regmap_bulk_write(idtcm->regmap, module + regaddr, buf, count);
59930dfa56SMin Li }
60930dfa56SMin Li 
contains_full_configuration(struct idtcm * idtcm,const struct firmware * fw)61794c3dffSMin Li static int contains_full_configuration(struct idtcm *idtcm,
62794c3dffSMin Li 				       const struct firmware *fw)
63251f4fe2SMin Li {
64251f4fe2SMin Li 	struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data;
65794c3dffSMin Li 	u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH);
66794c3dffSMin Li 	s32 full_count;
67251f4fe2SMin Li 	s32 count = 0;
68251f4fe2SMin Li 	u16 regaddr;
69251f4fe2SMin Li 	u8 loaddr;
70251f4fe2SMin Li 	s32 len;
71251f4fe2SMin Li 
72794c3dffSMin Li 	/* 4 bytes skipped every 0x80 */
73794c3dffSMin Li 	full_count = (scratch - GPIO_USER_CONTROL) -
74794c3dffSMin Li 		     ((scratch >> 7) - (GPIO_USER_CONTROL >> 7)) * 4;
75794c3dffSMin Li 
76251f4fe2SMin Li 	/* If the firmware contains 'full configuration' SM_RESET can be used
77251f4fe2SMin Li 	 * to ensure proper configuration.
78251f4fe2SMin Li 	 *
79251f4fe2SMin Li 	 * Full configuration is defined as the number of programmable
80251f4fe2SMin Li 	 * bytes within the configuration range minus page offset addr range.
81251f4fe2SMin Li 	 */
82251f4fe2SMin Li 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
83251f4fe2SMin Li 		regaddr = rec->hiaddr << 8;
84251f4fe2SMin Li 		regaddr |= rec->loaddr;
85251f4fe2SMin Li 
86251f4fe2SMin Li 		loaddr = rec->loaddr;
87251f4fe2SMin Li 
88251f4fe2SMin Li 		rec++;
89251f4fe2SMin Li 
90251f4fe2SMin Li 		/* Top (status registers) and bottom are read-only */
91794c3dffSMin Li 		if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch)
92251f4fe2SMin Li 			continue;
93251f4fe2SMin Li 
94251f4fe2SMin Li 		/* Page size 128, last 4 bytes of page skipped */
95251f4fe2SMin Li 		if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
96251f4fe2SMin Li 			continue;
97251f4fe2SMin Li 
98251f4fe2SMin Li 		count++;
99251f4fe2SMin Li 	}
100251f4fe2SMin Li 
101251f4fe2SMin Li 	return (count >= full_count);
102251f4fe2SMin Li }
103251f4fe2SMin Li 
char_array_to_timespec(u8 * buf,u8 count,struct timespec64 * ts)1043a6ba7dcSVincent Cheng static int char_array_to_timespec(u8 *buf,
1053a6ba7dcSVincent Cheng 				  u8 count,
1063a6ba7dcSVincent Cheng 				  struct timespec64 *ts)
1073a6ba7dcSVincent Cheng {
1083a6ba7dcSVincent Cheng 	u8 i;
1093a6ba7dcSVincent Cheng 	u64 nsec;
1103a6ba7dcSVincent Cheng 	time64_t sec;
1113a6ba7dcSVincent Cheng 
1123a6ba7dcSVincent Cheng 	if (count < TOD_BYTE_COUNT)
1133a6ba7dcSVincent Cheng 		return 1;
1143a6ba7dcSVincent Cheng 
1153a6ba7dcSVincent Cheng 	/* Sub-nanoseconds are in buf[0]. */
1163a6ba7dcSVincent Cheng 	nsec = buf[4];
1173a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
1183a6ba7dcSVincent Cheng 		nsec <<= 8;
1193a6ba7dcSVincent Cheng 		nsec |= buf[3 - i];
1203a6ba7dcSVincent Cheng 	}
1213a6ba7dcSVincent Cheng 
1223a6ba7dcSVincent Cheng 	sec = buf[10];
1233a6ba7dcSVincent Cheng 	for (i = 0; i < 5; i++) {
1243a6ba7dcSVincent Cheng 		sec <<= 8;
1253a6ba7dcSVincent Cheng 		sec |= buf[9 - i];
1263a6ba7dcSVincent Cheng 	}
1273a6ba7dcSVincent Cheng 
1283a6ba7dcSVincent Cheng 	ts->tv_sec = sec;
1293a6ba7dcSVincent Cheng 	ts->tv_nsec = nsec;
1303a6ba7dcSVincent Cheng 
1313a6ba7dcSVincent Cheng 	return 0;
1323a6ba7dcSVincent Cheng }
1333a6ba7dcSVincent Cheng 
timespec_to_char_array(struct timespec64 const * ts,u8 * buf,u8 count)1343a6ba7dcSVincent Cheng static int timespec_to_char_array(struct timespec64 const *ts,
1353a6ba7dcSVincent Cheng 				  u8 *buf,
1363a6ba7dcSVincent Cheng 				  u8 count)
1373a6ba7dcSVincent Cheng {
1383a6ba7dcSVincent Cheng 	u8 i;
1393a6ba7dcSVincent Cheng 	s32 nsec;
1403a6ba7dcSVincent Cheng 	time64_t sec;
1413a6ba7dcSVincent Cheng 
1423a6ba7dcSVincent Cheng 	if (count < TOD_BYTE_COUNT)
1433a6ba7dcSVincent Cheng 		return 1;
1443a6ba7dcSVincent Cheng 
1453a6ba7dcSVincent Cheng 	nsec = ts->tv_nsec;
1463a6ba7dcSVincent Cheng 	sec = ts->tv_sec;
1473a6ba7dcSVincent Cheng 
1483a6ba7dcSVincent Cheng 	/* Sub-nanoseconds are in buf[0]. */
1493a6ba7dcSVincent Cheng 	buf[0] = 0;
1503a6ba7dcSVincent Cheng 	for (i = 1; i < 5; i++) {
1513a6ba7dcSVincent Cheng 		buf[i] = nsec & 0xff;
1523a6ba7dcSVincent Cheng 		nsec >>= 8;
1533a6ba7dcSVincent Cheng 	}
1543a6ba7dcSVincent Cheng 
1553a6ba7dcSVincent Cheng 	for (i = 5; i < TOD_BYTE_COUNT; i++) {
1563a6ba7dcSVincent Cheng 
1573a6ba7dcSVincent Cheng 		buf[i] = sec & 0xff;
1583a6ba7dcSVincent Cheng 		sec >>= 8;
1593a6ba7dcSVincent Cheng 	}
1603a6ba7dcSVincent Cheng 
1613a6ba7dcSVincent Cheng 	return 0;
1623a6ba7dcSVincent Cheng }
1633a6ba7dcSVincent Cheng 
idtcm_strverscmp(const char * version1,const char * version2)1643cb2e6d9SMin Li static int idtcm_strverscmp(const char *version1, const char *version2)
1657ea5fda2SMin Li {
1663cb2e6d9SMin Li 	u8 ver1[3], ver2[3];
1673cb2e6d9SMin Li 	int i;
1687ea5fda2SMin Li 
1693cb2e6d9SMin Li 	if (sscanf(version1, "%hhu.%hhu.%hhu",
1703cb2e6d9SMin Li 		   &ver1[0], &ver1[1], &ver1[2]) != 3)
1713cb2e6d9SMin Li 		return -1;
1723cb2e6d9SMin Li 	if (sscanf(version2, "%hhu.%hhu.%hhu",
1733cb2e6d9SMin Li 		   &ver2[0], &ver2[1], &ver2[2]) != 3)
1747ea5fda2SMin Li 		return -1;
1757ea5fda2SMin Li 
1763cb2e6d9SMin Li 	for (i = 0; i < 3; i++) {
1773cb2e6d9SMin Li 		if (ver1[i] > ver2[i])
1783cb2e6d9SMin Li 			return 1;
1793cb2e6d9SMin Li 		if (ver1[i] < ver2[i])
1807ea5fda2SMin Li 			return -1;
1813cb2e6d9SMin Li 	}
1827ea5fda2SMin Li 
1833cb2e6d9SMin Li 	return 0;
1847ea5fda2SMin Li }
1857ea5fda2SMin Li 
idtcm_fw_version(const char * version)186794c3dffSMin Li static enum fw_version idtcm_fw_version(const char *version)
187794c3dffSMin Li {
188794c3dffSMin Li 	enum fw_version ver = V_DEFAULT;
189794c3dffSMin Li 
190794c3dffSMin Li 	if (idtcm_strverscmp(version, "4.8.7") >= 0)
191794c3dffSMin Li 		ver = V487;
192794c3dffSMin Li 
193794c3dffSMin Li 	if (idtcm_strverscmp(version, "5.2.0") >= 0)
194794c3dffSMin Li 		ver = V520;
195794c3dffSMin Li 
196794c3dffSMin Li 	return ver;
197794c3dffSMin Li }
198794c3dffSMin Li 
clear_boot_status(struct idtcm * idtcm)199251f4fe2SMin Li static int clear_boot_status(struct idtcm *idtcm)
200251f4fe2SMin Li {
201251f4fe2SMin Li 	u8 buf[4] = {0};
202251f4fe2SMin Li 
203fde3b3a7SVincent Cheng 	return idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
204251f4fe2SMin Li }
205251f4fe2SMin Li 
read_boot_status(struct idtcm * idtcm,u32 * status)206251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status)
207251f4fe2SMin Li {
208251f4fe2SMin Li 	int err;
209251f4fe2SMin Li 	u8 buf[4] = {0};
210251f4fe2SMin Li 
211251f4fe2SMin Li 	err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
212251f4fe2SMin Li 
213251f4fe2SMin Li 	*status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
214251f4fe2SMin Li 
215251f4fe2SMin Li 	return err;
216251f4fe2SMin Li }
217251f4fe2SMin Li 
wait_for_boot_status_ready(struct idtcm * idtcm)218251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm)
219251f4fe2SMin Li {
220251f4fe2SMin Li 	u32 status = 0;
221251f4fe2SMin Li 	u8 i = 30;	/* 30 * 100ms = 3s */
222251f4fe2SMin Li 	int err;
223251f4fe2SMin Li 
224251f4fe2SMin Li 	do {
225251f4fe2SMin Li 		err = read_boot_status(idtcm, &status);
226251f4fe2SMin Li 		if (err)
227251f4fe2SMin Li 			return err;
228251f4fe2SMin Li 
229251f4fe2SMin Li 		if (status == 0xA0)
230251f4fe2SMin Li 			return 0;
231251f4fe2SMin Li 
232251f4fe2SMin Li 		msleep(100);
233251f4fe2SMin Li 		i--;
234251f4fe2SMin Li 
235251f4fe2SMin Li 	} while (i);
236251f4fe2SMin Li 
237930dfa56SMin Li 	dev_warn(idtcm->dev, "%s timed out", __func__);
238251f4fe2SMin Li 
239251f4fe2SMin Li 	return -EBUSY;
240251f4fe2SMin Li }
241251f4fe2SMin Li 
arm_tod_read_trig_sel_refclk(struct idtcm_channel * channel,u8 ref)242bec67592SMin Li static int arm_tod_read_trig_sel_refclk(struct idtcm_channel *channel, u8 ref)
243930dfa56SMin Li {
244930dfa56SMin Li 	struct idtcm *idtcm = channel->idtcm;
245bec67592SMin Li 	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
246bec67592SMin Li 	u8 val = 0;
247930dfa56SMin Li 	int err;
248930dfa56SMin Li 
249930dfa56SMin Li 	val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
250930dfa56SMin Li 	val |= (ref << WR_REF_INDEX_SHIFT);
251930dfa56SMin Li 
252bec67592SMin Li 	err = idtcm_write(idtcm, channel->tod_read_secondary,
253bec67592SMin Li 			  TOD_READ_SECONDARY_SEL_CFG_0, &val, sizeof(val));
254930dfa56SMin Li 	if (err)
255930dfa56SMin Li 		return err;
256930dfa56SMin Li 
257bec67592SMin Li 	val = 0 | (SCSR_TOD_READ_TRIG_SEL_REFCLK << TOD_READ_TRIGGER_SHIFT);
258930dfa56SMin Li 
259bec67592SMin Li 	err = idtcm_write(idtcm, channel->tod_read_secondary, tod_read_cmd,
260bec67592SMin Li 			  &val, sizeof(val));
261bec67592SMin Li 	if (err)
262bec67592SMin Li 		dev_err(idtcm->dev, "%s: err = %d", __func__, err);
263bec67592SMin Li 
264930dfa56SMin Li 	return err;
265930dfa56SMin Li }
266930dfa56SMin Li 
is_single_shot(u8 mask)267bec67592SMin Li static bool is_single_shot(u8 mask)
268930dfa56SMin Li {
269bec67592SMin Li 	/* Treat single bit ToD masks as continuous trigger */
270d0bbe032SMin Li 	return !(mask <= 8 && is_power_of_2(mask));
271bec67592SMin Li }
272bec67592SMin Li 
idtcm_extts_enable(struct idtcm_channel * channel,struct ptp_clock_request * rq,int on)273bec67592SMin Li static int idtcm_extts_enable(struct idtcm_channel *channel,
274bec67592SMin Li 			      struct ptp_clock_request *rq, int on)
275bec67592SMin Li {
276bec67592SMin Li 	u8 index = rq->extts.index;
277bec67592SMin Li 	struct idtcm *idtcm;
278bec67592SMin Li 	u8 mask = 1 << index;
279930dfa56SMin Li 	int err = 0;
280bec67592SMin Li 	u8 old_mask;
281bec67592SMin Li 	int ref;
282930dfa56SMin Li 
283bec67592SMin Li 	idtcm = channel->idtcm;
284bec67592SMin Li 	old_mask = idtcm->extts_mask;
285bec67592SMin Li 
286bec67592SMin Li 	/* Reject requests with unsupported flags */
287bec67592SMin Li 	if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
288bec67592SMin Li 				PTP_RISING_EDGE |
289bec67592SMin Li 				PTP_FALLING_EDGE |
290bec67592SMin Li 				PTP_STRICT_FLAGS))
291bec67592SMin Li 		return -EOPNOTSUPP;
292bec67592SMin Li 
293bec67592SMin Li 	/* Reject requests to enable time stamping on falling edge */
294bec67592SMin Li 	if ((rq->extts.flags & PTP_ENABLE_FEATURE) &&
295bec67592SMin Li 	    (rq->extts.flags & PTP_FALLING_EDGE))
296bec67592SMin Li 		return -EOPNOTSUPP;
297bec67592SMin Li 
298bec67592SMin Li 	if (index >= MAX_TOD)
299930dfa56SMin Li 		return -EINVAL;
300930dfa56SMin Li 
301bec67592SMin Li 	if (on) {
302bec67592SMin Li 		/* Support triggering more than one TOD_0/1/2/3 by same pin */
303bec67592SMin Li 		/* Use the pin configured for the channel */
304bec67592SMin Li 		ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->tod);
305bec67592SMin Li 
306bec67592SMin Li 		if (ref < 0) {
307bec67592SMin Li 			dev_err(idtcm->dev, "%s: No valid pin found for TOD%d!\n",
308bec67592SMin Li 				__func__, channel->tod);
309bec67592SMin Li 			return -EBUSY;
310bec67592SMin Li 		}
311bec67592SMin Li 
312bec67592SMin Li 		err = arm_tod_read_trig_sel_refclk(&idtcm->channel[index], ref);
313bec67592SMin Li 
314930dfa56SMin Li 		if (err == 0) {
315930dfa56SMin Li 			idtcm->extts_mask |= mask;
316bec67592SMin Li 			idtcm->event_channel[index] = channel;
317bec67592SMin Li 			idtcm->channel[index].refn = ref;
318bec67592SMin Li 			idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
319930dfa56SMin Li 
320bec67592SMin Li 			if (old_mask)
321bec67592SMin Li 				return 0;
322bec67592SMin Li 
323930dfa56SMin Li 			schedule_delayed_work(&idtcm->extts_work,
324930dfa56SMin Li 					      msecs_to_jiffies(EXTTS_PERIOD_MS));
325bec67592SMin Li 		}
326bec67592SMin Li 	} else {
327bec67592SMin Li 		idtcm->extts_mask &= ~mask;
328bec67592SMin Li 		idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
329bec67592SMin Li 
330bec67592SMin Li 		if (idtcm->extts_mask == 0)
331bec67592SMin Li 			cancel_delayed_work(&idtcm->extts_work);
332bec67592SMin Li 	}
333930dfa56SMin Li 
334930dfa56SMin Li 	return err;
335930dfa56SMin Li }
336930dfa56SMin Li 
read_sys_apll_status(struct idtcm * idtcm,u8 * status)337797d3186SVincent Cheng static int read_sys_apll_status(struct idtcm *idtcm, u8 *status)
338797d3186SVincent Cheng {
339797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status,
340797d3186SVincent Cheng 			  sizeof(u8));
341797d3186SVincent Cheng }
342797d3186SVincent Cheng 
read_sys_dpll_status(struct idtcm * idtcm,u8 * status)343797d3186SVincent Cheng static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status)
344797d3186SVincent Cheng {
345797d3186SVincent Cheng 	return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8));
346797d3186SVincent Cheng }
347797d3186SVincent Cheng 
wait_for_sys_apll_dpll_lock(struct idtcm * idtcm)348797d3186SVincent Cheng static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm)
349797d3186SVincent Cheng {
350797d3186SVincent Cheng 	unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS);
351797d3186SVincent Cheng 	u8 apll = 0;
352797d3186SVincent Cheng 	u8 dpll = 0;
353797d3186SVincent Cheng 	int err;
354797d3186SVincent Cheng 
355797d3186SVincent Cheng 	do {
356797d3186SVincent Cheng 		err = read_sys_apll_status(idtcm, &apll);
357797d3186SVincent Cheng 		if (err)
358797d3186SVincent Cheng 			return err;
359797d3186SVincent Cheng 
360797d3186SVincent Cheng 		err = read_sys_dpll_status(idtcm, &dpll);
361797d3186SVincent Cheng 		if (err)
362797d3186SVincent Cheng 			return err;
363797d3186SVincent Cheng 
364797d3186SVincent Cheng 		apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK;
365797d3186SVincent Cheng 		dpll &= DPLL_SYS_STATE_MASK;
366797d3186SVincent Cheng 
367794c3dffSMin Li 		if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED &&
368794c3dffSMin Li 		    dpll == DPLL_STATE_LOCKED) {
369797d3186SVincent Cheng 			return 0;
370797d3186SVincent Cheng 		} else if (dpll == DPLL_STATE_FREERUN ||
371797d3186SVincent Cheng 			   dpll == DPLL_STATE_HOLDOVER ||
372797d3186SVincent Cheng 			   dpll == DPLL_STATE_OPEN_LOOP) {
373930dfa56SMin Li 			dev_warn(idtcm->dev,
374797d3186SVincent Cheng 				"No wait state: DPLL_SYS_STATE %d", dpll);
375797d3186SVincent Cheng 			return -EPERM;
376797d3186SVincent Cheng 		}
377797d3186SVincent Cheng 
378797d3186SVincent Cheng 		msleep(LOCK_POLL_INTERVAL_MS);
379797d3186SVincent Cheng 	} while (time_is_after_jiffies(timeout));
380797d3186SVincent Cheng 
381930dfa56SMin Li 	dev_warn(idtcm->dev,
382797d3186SVincent Cheng 		 "%d ms lock timeout: SYS APLL Loss Lock %d  SYS DPLL state %d",
383797d3186SVincent Cheng 		 LOCK_TIMEOUT_MS, apll, dpll);
384797d3186SVincent Cheng 
385797d3186SVincent Cheng 	return -ETIME;
386797d3186SVincent Cheng }
387797d3186SVincent Cheng 
wait_for_chip_ready(struct idtcm * idtcm)388797d3186SVincent Cheng static void wait_for_chip_ready(struct idtcm *idtcm)
389797d3186SVincent Cheng {
390797d3186SVincent Cheng 	if (wait_for_boot_status_ready(idtcm))
391930dfa56SMin Li 		dev_warn(idtcm->dev, "BOOT_STATUS != 0xA0");
392797d3186SVincent Cheng 
393797d3186SVincent Cheng 	if (wait_for_sys_apll_dpll_lock(idtcm))
394930dfa56SMin Li 		dev_warn(idtcm->dev,
395797d3186SVincent Cheng 			 "Continuing while SYS APLL/DPLL is not locked");
396797d3186SVincent Cheng }
397797d3186SVincent Cheng 
_idtcm_gettime_triggered(struct idtcm_channel * channel,struct timespec64 * ts)398bec67592SMin Li static int _idtcm_gettime_triggered(struct idtcm_channel *channel,
399bec67592SMin Li 				    struct timespec64 *ts)
400bec67592SMin Li {
401bec67592SMin Li 	struct idtcm *idtcm = channel->idtcm;
402bec67592SMin Li 	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
403bec67592SMin Li 	u8 buf[TOD_BYTE_COUNT];
404bec67592SMin Li 	u8 trigger;
405bec67592SMin Li 	int err;
406bec67592SMin Li 
407bec67592SMin Li 	err = idtcm_read(idtcm, channel->tod_read_secondary,
408bec67592SMin Li 			 tod_read_cmd, &trigger, sizeof(trigger));
409bec67592SMin Li 	if (err)
410bec67592SMin Li 		return err;
411bec67592SMin Li 
412bec67592SMin Li 	if (trigger & TOD_READ_TRIGGER_MASK)
413bec67592SMin Li 		return -EBUSY;
414bec67592SMin Li 
415bec67592SMin Li 	err = idtcm_read(idtcm, channel->tod_read_secondary,
416bec67592SMin Li 			 TOD_READ_SECONDARY_BASE, buf, sizeof(buf));
417bec67592SMin Li 	if (err)
418bec67592SMin Li 		return err;
419bec67592SMin Li 
420bec67592SMin Li 	return char_array_to_timespec(buf, sizeof(buf), ts);
421bec67592SMin Li }
422bec67592SMin Li 
_idtcm_gettime(struct idtcm_channel * channel,struct timespec64 * ts,u8 timeout)4233a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel,
424930dfa56SMin Li 			  struct timespec64 *ts, u8 timeout)
4253a6ba7dcSVincent Cheng {
4263a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
427794c3dffSMin Li 	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
4283a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
4293a6ba7dcSVincent Cheng 	u8 trigger;
4303a6ba7dcSVincent Cheng 	int err;
4313a6ba7dcSVincent Cheng 
4327ea5fda2SMin Li 	/* wait trigger to be 0 */
433930dfa56SMin Li 	do {
434930dfa56SMin Li 		if (timeout-- == 0)
435930dfa56SMin Li 			return -EIO;
436930dfa56SMin Li 
4377ea5fda2SMin Li 		if (idtcm->calculate_overhead_flag)
4387ea5fda2SMin Li 			idtcm->start_time = ktime_get_raw();
4397ea5fda2SMin Li 
4407ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_read_primary,
441794c3dffSMin Li 				 tod_read_cmd, &trigger,
4427ea5fda2SMin Li 				 sizeof(trigger));
4433a6ba7dcSVincent Cheng 		if (err)
4443a6ba7dcSVincent Cheng 			return err;
445930dfa56SMin Li 	} while (trigger & TOD_READ_TRIGGER_MASK);
4463a6ba7dcSVincent Cheng 
4473a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->tod_read_primary,
448bec67592SMin Li 			 TOD_READ_PRIMARY_BASE, buf, sizeof(buf));
4493a6ba7dcSVincent Cheng 	if (err)
4503a6ba7dcSVincent Cheng 		return err;
4513a6ba7dcSVincent Cheng 
4523a6ba7dcSVincent Cheng 	err = char_array_to_timespec(buf, sizeof(buf), ts);
4533a6ba7dcSVincent Cheng 
4543a6ba7dcSVincent Cheng 	return err;
4553a6ba7dcSVincent Cheng }
4563a6ba7dcSVincent Cheng 
idtcm_extts_check_channel(struct idtcm * idtcm,u8 todn)457930dfa56SMin Li static int idtcm_extts_check_channel(struct idtcm *idtcm, u8 todn)
458930dfa56SMin Li {
459930dfa56SMin Li 	struct idtcm_channel *ptp_channel, *extts_channel;
460930dfa56SMin Li 	struct ptp_clock_event event;
461930dfa56SMin Li 	struct timespec64 ts;
462930dfa56SMin Li 	u32 dco_delay = 0;
463930dfa56SMin Li 	int err;
464930dfa56SMin Li 
465930dfa56SMin Li 	extts_channel = &idtcm->channel[todn];
466930dfa56SMin Li 	ptp_channel = idtcm->event_channel[todn];
467bec67592SMin Li 
468930dfa56SMin Li 	if (extts_channel == ptp_channel)
469930dfa56SMin Li 		dco_delay = ptp_channel->dco_delay;
470930dfa56SMin Li 
471bec67592SMin Li 	err = _idtcm_gettime_triggered(extts_channel, &ts);
472bec67592SMin Li 	if (err)
473bec67592SMin Li 		return err;
474bec67592SMin Li 
475bec67592SMin Li 	/* Triggered - save timestamp */
476930dfa56SMin Li 	event.type = PTP_CLOCK_EXTTS;
477930dfa56SMin Li 	event.index = todn;
478930dfa56SMin Li 	event.timestamp = timespec64_to_ns(&ts) - dco_delay;
479930dfa56SMin Li 	ptp_clock_event(ptp_channel->ptp_clock, &event);
480bec67592SMin Li 
481930dfa56SMin Li 	return err;
482930dfa56SMin Li }
483930dfa56SMin Li 
_idtcm_gettime_immediate(struct idtcm_channel * channel,struct timespec64 * ts)484930dfa56SMin Li static int _idtcm_gettime_immediate(struct idtcm_channel *channel,
485930dfa56SMin Li 				    struct timespec64 *ts)
486930dfa56SMin Li {
487930dfa56SMin Li 	struct idtcm *idtcm = channel->idtcm;
488bec67592SMin Li 
489bec67592SMin Li 	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
490bec67592SMin Li 	u8 val = (SCSR_TOD_READ_TRIG_SEL_IMMEDIATE << TOD_READ_TRIGGER_SHIFT);
491930dfa56SMin Li 	int err;
492930dfa56SMin Li 
493bec67592SMin Li 	err = idtcm_write(idtcm, channel->tod_read_primary,
494bec67592SMin Li 			  tod_read_cmd, &val, sizeof(val));
495bec67592SMin Li 	if (err)
496930dfa56SMin Li 		return err;
497bec67592SMin Li 
498bec67592SMin Li 	return _idtcm_gettime(channel, ts, 10);
499930dfa56SMin Li }
500930dfa56SMin Li 
_sync_pll_output(struct idtcm * idtcm,u8 pll,u8 sync_src,u8 qn,u8 qn_plus_1)5013a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm,
5023a6ba7dcSVincent Cheng 			    u8 pll,
5033a6ba7dcSVincent Cheng 			    u8 sync_src,
5043a6ba7dcSVincent Cheng 			    u8 qn,
5053a6ba7dcSVincent Cheng 			    u8 qn_plus_1)
5063a6ba7dcSVincent Cheng {
5073a6ba7dcSVincent Cheng 	int err;
5083a6ba7dcSVincent Cheng 	u8 val;
5093a6ba7dcSVincent Cheng 	u16 sync_ctrl0;
5103a6ba7dcSVincent Cheng 	u16 sync_ctrl1;
5117ea5fda2SMin Li 	u8 temp;
5123a6ba7dcSVincent Cheng 
51377fdb168SVincent Cheng 	if (qn == 0 && qn_plus_1 == 0)
5143a6ba7dcSVincent Cheng 		return 0;
5153a6ba7dcSVincent Cheng 
5163a6ba7dcSVincent Cheng 	switch (pll) {
5173a6ba7dcSVincent Cheng 	case 0:
5183a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
5193a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
5203a6ba7dcSVincent Cheng 		break;
5213a6ba7dcSVincent Cheng 	case 1:
5223a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
5233a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
5243a6ba7dcSVincent Cheng 		break;
5253a6ba7dcSVincent Cheng 	case 2:
5263a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
5273a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
5283a6ba7dcSVincent Cheng 		break;
5293a6ba7dcSVincent Cheng 	case 3:
5303a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
5313a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
5323a6ba7dcSVincent Cheng 		break;
5333a6ba7dcSVincent Cheng 	case 4:
5343a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
5353a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
5363a6ba7dcSVincent Cheng 		break;
5373a6ba7dcSVincent Cheng 	case 5:
5383a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
5393a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
5403a6ba7dcSVincent Cheng 		break;
5413a6ba7dcSVincent Cheng 	case 6:
5423a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
5433a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
5443a6ba7dcSVincent Cheng 		break;
5453a6ba7dcSVincent Cheng 	case 7:
5463a6ba7dcSVincent Cheng 		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
5473a6ba7dcSVincent Cheng 		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
5483a6ba7dcSVincent Cheng 		break;
5493a6ba7dcSVincent Cheng 	default:
5503a6ba7dcSVincent Cheng 		return -EINVAL;
5513a6ba7dcSVincent Cheng 	}
5523a6ba7dcSVincent Cheng 
5533a6ba7dcSVincent Cheng 	val = SYNCTRL1_MASTER_SYNC_RST;
5543a6ba7dcSVincent Cheng 
5553a6ba7dcSVincent Cheng 	/* Place master sync in reset */
5563a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5573a6ba7dcSVincent Cheng 	if (err)
5583a6ba7dcSVincent Cheng 		return err;
5593a6ba7dcSVincent Cheng 
5603a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
5613a6ba7dcSVincent Cheng 	if (err)
5623a6ba7dcSVincent Cheng 		return err;
5633a6ba7dcSVincent Cheng 
5643a6ba7dcSVincent Cheng 	/* Set sync trigger mask */
5653a6ba7dcSVincent Cheng 	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
5663a6ba7dcSVincent Cheng 
5673a6ba7dcSVincent Cheng 	if (qn)
5683a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
5693a6ba7dcSVincent Cheng 
5703a6ba7dcSVincent Cheng 	if (qn_plus_1)
5713a6ba7dcSVincent Cheng 		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
5723a6ba7dcSVincent Cheng 
5733a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
5743a6ba7dcSVincent Cheng 	if (err)
5753a6ba7dcSVincent Cheng 		return err;
5763a6ba7dcSVincent Cheng 
5777ea5fda2SMin Li 	/* PLL5 can have OUT8 as second additional output. */
57877fdb168SVincent Cheng 	if (pll == 5 && qn_plus_1 != 0) {
5797ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
5807ea5fda2SMin Li 				 &temp, sizeof(temp));
5817ea5fda2SMin Li 		if (err)
5827ea5fda2SMin Li 			return err;
5837ea5fda2SMin Li 
5847ea5fda2SMin Li 		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
5857ea5fda2SMin Li 
5867ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5877ea5fda2SMin Li 				  &temp, sizeof(temp));
5887ea5fda2SMin Li 		if (err)
5897ea5fda2SMin Li 			return err;
5907ea5fda2SMin Li 
5917ea5fda2SMin Li 		temp |= Q9_TO_Q8_SYNC_TRIG;
5927ea5fda2SMin Li 
5937ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
5947ea5fda2SMin Li 				  &temp, sizeof(temp));
5957ea5fda2SMin Li 		if (err)
5967ea5fda2SMin Li 			return err;
5977ea5fda2SMin Li 	}
5987ea5fda2SMin Li 
5997ea5fda2SMin Li 	/* PLL6 can have OUT11 as second additional output. */
60077fdb168SVincent Cheng 	if (pll == 6 && qn_plus_1 != 0) {
6017ea5fda2SMin Li 		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
6027ea5fda2SMin Li 				 &temp, sizeof(temp));
6037ea5fda2SMin Li 		if (err)
6047ea5fda2SMin Li 			return err;
6057ea5fda2SMin Li 
6067ea5fda2SMin Li 		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
6077ea5fda2SMin Li 
6087ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
6097ea5fda2SMin Li 				  &temp, sizeof(temp));
6107ea5fda2SMin Li 		if (err)
6117ea5fda2SMin Li 			return err;
6127ea5fda2SMin Li 
6137ea5fda2SMin Li 		temp |= Q10_TO_Q11_SYNC_TRIG;
6147ea5fda2SMin Li 
6157ea5fda2SMin Li 		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
6167ea5fda2SMin Li 				  &temp, sizeof(temp));
6177ea5fda2SMin Li 		if (err)
6187ea5fda2SMin Li 			return err;
6197ea5fda2SMin Li 	}
6207ea5fda2SMin Li 
6213a6ba7dcSVincent Cheng 	/* Place master sync out of reset */
6223a6ba7dcSVincent Cheng 	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
6233a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
6243a6ba7dcSVincent Cheng 
6253a6ba7dcSVincent Cheng 	return err;
6263a6ba7dcSVincent Cheng }
6273a6ba7dcSVincent Cheng 
idtcm_sync_pps_output(struct idtcm_channel * channel)6283a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel)
6293a6ba7dcSVincent Cheng {
6303a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
6313a6ba7dcSVincent Cheng 	u8 pll;
6323a6ba7dcSVincent Cheng 	u8 qn;
6333a6ba7dcSVincent Cheng 	u8 qn_plus_1;
6343a6ba7dcSVincent Cheng 	int err = 0;
6357ea5fda2SMin Li 	u8 out8_mux = 0;
6367ea5fda2SMin Li 	u8 out11_mux = 0;
6377ea5fda2SMin Li 	u8 temp;
6383a6ba7dcSVincent Cheng 	u16 output_mask = channel->output_mask;
6393a6ba7dcSVincent Cheng 
6407ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
6417ea5fda2SMin Li 			 &temp, sizeof(temp));
6427ea5fda2SMin Li 	if (err)
6437ea5fda2SMin Li 		return err;
6447ea5fda2SMin Li 
6457ea5fda2SMin Li 	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6467ea5fda2SMin Li 	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6477ea5fda2SMin Li 		out8_mux = 1;
6487ea5fda2SMin Li 
6497ea5fda2SMin Li 	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
6507ea5fda2SMin Li 			 &temp, sizeof(temp));
6517ea5fda2SMin Li 	if (err)
6527ea5fda2SMin Li 		return err;
6537ea5fda2SMin Li 
6547ea5fda2SMin Li 	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
6557ea5fda2SMin Li 	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
6567ea5fda2SMin Li 		out11_mux = 1;
6573a6ba7dcSVincent Cheng 
6583a6ba7dcSVincent Cheng 	for (pll = 0; pll < 8; pll++) {
6597ea5fda2SMin Li 		qn = 0;
6607ea5fda2SMin Li 		qn_plus_1 = 0;
6613a6ba7dcSVincent Cheng 
6623a6ba7dcSVincent Cheng 		if (pll < 4) {
6633a6ba7dcSVincent Cheng 			/* First 4 pll has 2 outputs */
6647ea5fda2SMin Li 			qn = output_mask & 0x1;
6657ea5fda2SMin Li 			output_mask = output_mask >> 1;
6663a6ba7dcSVincent Cheng 			qn_plus_1 = output_mask & 0x1;
6673a6ba7dcSVincent Cheng 			output_mask = output_mask >> 1;
6687ea5fda2SMin Li 		} else if (pll == 4) {
6697ea5fda2SMin Li 			if (out8_mux == 0) {
6707ea5fda2SMin Li 				qn = output_mask & 0x1;
6717ea5fda2SMin Li 				output_mask = output_mask >> 1;
6727ea5fda2SMin Li 			}
6737ea5fda2SMin Li 		} else if (pll == 5) {
6747ea5fda2SMin Li 			if (out8_mux) {
6757ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6767ea5fda2SMin Li 				output_mask = output_mask >> 1;
6777ea5fda2SMin Li 			}
6787ea5fda2SMin Li 			qn = output_mask & 0x1;
6797ea5fda2SMin Li 			output_mask = output_mask >> 1;
6807ea5fda2SMin Li 		} else if (pll == 6) {
6817ea5fda2SMin Li 			qn = output_mask & 0x1;
6827ea5fda2SMin Li 			output_mask = output_mask >> 1;
6837ea5fda2SMin Li 			if (out11_mux) {
6847ea5fda2SMin Li 				qn_plus_1 = output_mask & 0x1;
6857ea5fda2SMin Li 				output_mask = output_mask >> 1;
6867ea5fda2SMin Li 			}
6877ea5fda2SMin Li 		} else if (pll == 7) {
6887ea5fda2SMin Li 			if (out11_mux == 0) {
6897ea5fda2SMin Li 				qn = output_mask & 0x1;
6907ea5fda2SMin Li 				output_mask = output_mask >> 1;
6917ea5fda2SMin Li 			}
6923a6ba7dcSVincent Cheng 		}
6933a6ba7dcSVincent Cheng 
69477fdb168SVincent Cheng 		if (qn != 0 || qn_plus_1 != 0)
695794c3dffSMin Li 			err = _sync_pll_output(idtcm, pll, channel->sync_src,
696794c3dffSMin Li 					       qn, qn_plus_1);
6973a6ba7dcSVincent Cheng 
6983a6ba7dcSVincent Cheng 		if (err)
6993a6ba7dcSVincent Cheng 			return err;
7003a6ba7dcSVincent Cheng 	}
7013a6ba7dcSVincent Cheng 
7023a6ba7dcSVincent Cheng 	return err;
7033a6ba7dcSVincent Cheng }
7043a6ba7dcSVincent Cheng 
_idtcm_set_dpll_hw_tod(struct idtcm_channel * channel,struct timespec64 const * ts,enum hw_tod_write_trig_sel wr_trig)7057ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
7063a6ba7dcSVincent Cheng 				  struct timespec64 const *ts,
7073a6ba7dcSVincent Cheng 				  enum hw_tod_write_trig_sel wr_trig)
7083a6ba7dcSVincent Cheng {
7093a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
7103a6ba7dcSVincent Cheng 	u8 buf[TOD_BYTE_COUNT];
7113a6ba7dcSVincent Cheng 	u8 cmd;
7123a6ba7dcSVincent Cheng 	int err;
7133a6ba7dcSVincent Cheng 	struct timespec64 local_ts = *ts;
7143a6ba7dcSVincent Cheng 	s64 total_overhead_ns;
7153a6ba7dcSVincent Cheng 
7163a6ba7dcSVincent Cheng 	/* Configure HW TOD write trigger. */
7173a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
7183a6ba7dcSVincent Cheng 			 &cmd, sizeof(cmd));
7193a6ba7dcSVincent Cheng 	if (err)
7203a6ba7dcSVincent Cheng 		return err;
7213a6ba7dcSVincent Cheng 
7223a6ba7dcSVincent Cheng 	cmd &= ~(0x0f);
7233a6ba7dcSVincent Cheng 	cmd |= wr_trig | 0x08;
7243a6ba7dcSVincent Cheng 
7253a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
7263a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
7273a6ba7dcSVincent Cheng 	if (err)
7283a6ba7dcSVincent Cheng 		return err;
7293a6ba7dcSVincent Cheng 
7303a6ba7dcSVincent Cheng 	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
7313a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7323a6ba7dcSVincent Cheng 		if (err)
7333a6ba7dcSVincent Cheng 			return err;
7343a6ba7dcSVincent Cheng 
7353a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
7363a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7373a6ba7dcSVincent Cheng 		if (err)
7383a6ba7dcSVincent Cheng 			return err;
7393a6ba7dcSVincent Cheng 	}
7403a6ba7dcSVincent Cheng 
7413a6ba7dcSVincent Cheng 	/* ARM HW TOD write trigger. */
7423a6ba7dcSVincent Cheng 	cmd &= ~(0x08);
7433a6ba7dcSVincent Cheng 
7443a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
7453a6ba7dcSVincent Cheng 			  &cmd, sizeof(cmd));
7463a6ba7dcSVincent Cheng 
7473a6ba7dcSVincent Cheng 	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
7483a6ba7dcSVincent Cheng 		if (idtcm->calculate_overhead_flag) {
7491ece2fbeSVincent Cheng 			/* Assumption: I2C @ 400KHz */
7507260d1c8SMin Li 			ktime_t diff = ktime_sub(ktime_get_raw(),
7517260d1c8SMin Li 						 idtcm->start_time);
7527260d1c8SMin Li 			total_overhead_ns =  ktime_to_ns(diff)
7533a6ba7dcSVincent Cheng 					     + idtcm->tod_write_overhead_ns
7543a6ba7dcSVincent Cheng 					     + SETTIME_CORRECTION;
7553a6ba7dcSVincent Cheng 
7563a6ba7dcSVincent Cheng 			timespec64_add_ns(&local_ts, total_overhead_ns);
7573a6ba7dcSVincent Cheng 
7583a6ba7dcSVincent Cheng 			idtcm->calculate_overhead_flag = 0;
7593a6ba7dcSVincent Cheng 		}
7603a6ba7dcSVincent Cheng 
7613a6ba7dcSVincent Cheng 		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7623a6ba7dcSVincent Cheng 		if (err)
7633a6ba7dcSVincent Cheng 			return err;
7643a6ba7dcSVincent Cheng 
7653a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->hw_dpll_n,
7663a6ba7dcSVincent Cheng 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
7673a6ba7dcSVincent Cheng 	}
7683a6ba7dcSVincent Cheng 
7693a6ba7dcSVincent Cheng 	return err;
7703a6ba7dcSVincent Cheng }
7713a6ba7dcSVincent Cheng 
_idtcm_set_dpll_scsr_tod(struct idtcm_channel * channel,struct timespec64 const * ts,enum scsr_tod_write_trig_sel wr_trig,enum scsr_tod_write_type_sel wr_type)7727ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
7737ea5fda2SMin Li 				    struct timespec64 const *ts,
7747ea5fda2SMin Li 				    enum scsr_tod_write_trig_sel wr_trig,
7757ea5fda2SMin Li 				    enum scsr_tod_write_type_sel wr_type)
7767ea5fda2SMin Li {
7777ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
7787ea5fda2SMin Li 	unsigned char buf[TOD_BYTE_COUNT], cmd;
7797ea5fda2SMin Li 	struct timespec64 local_ts = *ts;
7807ea5fda2SMin Li 	int err, count = 0;
7817ea5fda2SMin Li 
7827ea5fda2SMin Li 	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
7837ea5fda2SMin Li 
7847ea5fda2SMin Li 	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
7857ea5fda2SMin Li 	if (err)
7867ea5fda2SMin Li 		return err;
7877ea5fda2SMin Li 
7887ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
7897ea5fda2SMin Li 			  buf, sizeof(buf));
7907ea5fda2SMin Li 	if (err)
7917ea5fda2SMin Li 		return err;
7927ea5fda2SMin Li 
7937ea5fda2SMin Li 	/* Trigger the write operation. */
7947ea5fda2SMin Li 	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
7957ea5fda2SMin Li 			 &cmd, sizeof(cmd));
7967ea5fda2SMin Li 	if (err)
7977ea5fda2SMin Li 		return err;
7987ea5fda2SMin Li 
7997ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
8007ea5fda2SMin Li 	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
8017ea5fda2SMin Li 	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
8027ea5fda2SMin Li 	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
8037ea5fda2SMin Li 
8047ea5fda2SMin Li 	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
8057ea5fda2SMin Li 			   &cmd, sizeof(cmd));
8067ea5fda2SMin Li 	if (err)
8077ea5fda2SMin Li 		return err;
8087ea5fda2SMin Li 
8097ea5fda2SMin Li 	/* Wait for the operation to complete. */
8107ea5fda2SMin Li 	while (1) {
8117ea5fda2SMin Li 		/* pps trigger takes up to 1 sec to complete */
8127ea5fda2SMin Li 		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
8137ea5fda2SMin Li 			msleep(50);
8147ea5fda2SMin Li 
8157ea5fda2SMin Li 		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
8167ea5fda2SMin Li 				 &cmd, sizeof(cmd));
8177ea5fda2SMin Li 		if (err)
8187ea5fda2SMin Li 			return err;
8197ea5fda2SMin Li 
820251f4fe2SMin Li 		if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
8217ea5fda2SMin Li 			break;
8227ea5fda2SMin Li 
8237ea5fda2SMin Li 		if (++count > 20) {
824930dfa56SMin Li 			dev_err(idtcm->dev,
8251c49d3e9SVincent Cheng 				"Timed out waiting for the write counter");
8267ea5fda2SMin Li 			return -EIO;
8277ea5fda2SMin Li 		}
8287ea5fda2SMin Li 	}
8297ea5fda2SMin Li 
8307ea5fda2SMin Li 	return 0;
8317ea5fda2SMin Li }
8327ea5fda2SMin Li 
get_output_base_addr(enum fw_version ver,u8 outn)833794c3dffSMin Li static int get_output_base_addr(enum fw_version ver, u8 outn)
8347260d1c8SMin Li {
8357260d1c8SMin Li 	int base;
8367260d1c8SMin Li 
8377260d1c8SMin Li 	switch (outn) {
8387260d1c8SMin Li 	case 0:
839794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_0);
8407260d1c8SMin Li 		break;
8417260d1c8SMin Li 	case 1:
842794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_1);
8437260d1c8SMin Li 		break;
8447260d1c8SMin Li 	case 2:
845794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_2);
8467260d1c8SMin Li 		break;
8477260d1c8SMin Li 	case 3:
848794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_3);
8497260d1c8SMin Li 		break;
8507260d1c8SMin Li 	case 4:
851794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_4);
8527260d1c8SMin Li 		break;
8537260d1c8SMin Li 	case 5:
854794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_5);
8557260d1c8SMin Li 		break;
8567260d1c8SMin Li 	case 6:
857794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_6);
8587260d1c8SMin Li 		break;
8597260d1c8SMin Li 	case 7:
860794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_7);
8617260d1c8SMin Li 		break;
8627260d1c8SMin Li 	case 8:
863794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_8);
8647260d1c8SMin Li 		break;
8657260d1c8SMin Li 	case 9:
866794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_9);
8677260d1c8SMin Li 		break;
8687260d1c8SMin Li 	case 10:
869794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_10);
8707260d1c8SMin Li 		break;
8717260d1c8SMin Li 	case 11:
872794c3dffSMin Li 		base = IDTCM_FW_REG(ver, V520, OUTPUT_11);
8737260d1c8SMin Li 		break;
8747260d1c8SMin Li 	default:
8757260d1c8SMin Li 		base = -EINVAL;
8767260d1c8SMin Li 	}
8777260d1c8SMin Li 
8787260d1c8SMin Li 	return base;
8797260d1c8SMin Li }
8807260d1c8SMin Li 
_idtcm_settime_deprecated(struct idtcm_channel * channel,struct timespec64 const * ts)881da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
882251f4fe2SMin Li 				     struct timespec64 const *ts)
8833a6ba7dcSVincent Cheng {
8843a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
8853a6ba7dcSVincent Cheng 	int err;
8863a6ba7dcSVincent Cheng 
887251f4fe2SMin Li 	err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
8887ea5fda2SMin Li 	if (err) {
889930dfa56SMin Li 		dev_err(idtcm->dev,
8901c49d3e9SVincent Cheng 			"%s: Set HW ToD failed", __func__);
8913a6ba7dcSVincent Cheng 		return err;
8927ea5fda2SMin Li 	}
8933a6ba7dcSVincent Cheng 
8947ea5fda2SMin Li 	return idtcm_sync_pps_output(channel);
8957ea5fda2SMin Li }
8963a6ba7dcSVincent Cheng 
_idtcm_settime(struct idtcm_channel * channel,struct timespec64 const * ts,enum scsr_tod_write_type_sel wr_type)897da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel,
8987ea5fda2SMin Li 			  struct timespec64 const *ts,
8997ea5fda2SMin Li 			  enum scsr_tod_write_type_sel wr_type)
9007ea5fda2SMin Li {
9017ea5fda2SMin Li 	return _idtcm_set_dpll_scsr_tod(channel, ts,
9027ea5fda2SMin Li 					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
9037ea5fda2SMin Li 					wr_type);
9043a6ba7dcSVincent Cheng }
9053a6ba7dcSVincent Cheng 
idtcm_set_phase_pull_in_offset(struct idtcm_channel * channel,s32 offset_ns)9063a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
9073a6ba7dcSVincent Cheng 					  s32 offset_ns)
9083a6ba7dcSVincent Cheng {
9093a6ba7dcSVincent Cheng 	int err;
9103a6ba7dcSVincent Cheng 	int i;
9113a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9123a6ba7dcSVincent Cheng 	u8 buf[4];
9133a6ba7dcSVincent Cheng 
9143a6ba7dcSVincent Cheng 	for (i = 0; i < 4; i++) {
9153a6ba7dcSVincent Cheng 		buf[i] = 0xff & (offset_ns);
9163a6ba7dcSVincent Cheng 		offset_ns >>= 8;
9173a6ba7dcSVincent Cheng 	}
9183a6ba7dcSVincent Cheng 
9193a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
9203a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
9213a6ba7dcSVincent Cheng 
9223a6ba7dcSVincent Cheng 	return err;
9233a6ba7dcSVincent Cheng }
9243a6ba7dcSVincent Cheng 
idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel * channel,u32 max_ffo_ppb)9253a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
9263a6ba7dcSVincent Cheng 					       u32 max_ffo_ppb)
9273a6ba7dcSVincent Cheng {
9283a6ba7dcSVincent Cheng 	int err;
9293a6ba7dcSVincent Cheng 	u8 i;
9303a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9313a6ba7dcSVincent Cheng 	u8 buf[3];
9323a6ba7dcSVincent Cheng 
9333a6ba7dcSVincent Cheng 	if (max_ffo_ppb & 0xff000000)
9343a6ba7dcSVincent Cheng 		max_ffo_ppb = 0;
9353a6ba7dcSVincent Cheng 
9363a6ba7dcSVincent Cheng 	for (i = 0; i < 3; i++) {
9373a6ba7dcSVincent Cheng 		buf[i] = 0xff & (max_ffo_ppb);
9383a6ba7dcSVincent Cheng 		max_ffo_ppb >>= 8;
9393a6ba7dcSVincent Cheng 	}
9403a6ba7dcSVincent Cheng 
9413a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9423a6ba7dcSVincent Cheng 			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
9433a6ba7dcSVincent Cheng 
9443a6ba7dcSVincent Cheng 	return err;
9453a6ba7dcSVincent Cheng }
9463a6ba7dcSVincent Cheng 
idtcm_start_phase_pull_in(struct idtcm_channel * channel)9473a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
9483a6ba7dcSVincent Cheng {
9493a6ba7dcSVincent Cheng 	int err;
9503a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
9513a6ba7dcSVincent Cheng 	u8 buf;
9523a6ba7dcSVincent Cheng 
9533a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
9543a6ba7dcSVincent Cheng 			 &buf, sizeof(buf));
9553a6ba7dcSVincent Cheng 	if (err)
9563a6ba7dcSVincent Cheng 		return err;
9573a6ba7dcSVincent Cheng 
9583a6ba7dcSVincent Cheng 	if (buf == 0) {
9593a6ba7dcSVincent Cheng 		buf = 0x01;
9603a6ba7dcSVincent Cheng 		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
9613a6ba7dcSVincent Cheng 				  PULL_IN_CTRL, &buf, sizeof(buf));
9623a6ba7dcSVincent Cheng 	} else {
9633a6ba7dcSVincent Cheng 		err = -EBUSY;
9643a6ba7dcSVincent Cheng 	}
9653a6ba7dcSVincent Cheng 
9663a6ba7dcSVincent Cheng 	return err;
9673a6ba7dcSVincent Cheng }
9683a6ba7dcSVincent Cheng 
do_phase_pull_in_fw(struct idtcm_channel * channel,s32 offset_ns,u32 max_ffo_ppb)969da9facf1SMin Li static int do_phase_pull_in_fw(struct idtcm_channel *channel,
9703a6ba7dcSVincent Cheng 			       s32 offset_ns,
9713a6ba7dcSVincent Cheng 			       u32 max_ffo_ppb)
9723a6ba7dcSVincent Cheng {
9733a6ba7dcSVincent Cheng 	int err;
9743a6ba7dcSVincent Cheng 
9753a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
9763a6ba7dcSVincent Cheng 	if (err)
9773a6ba7dcSVincent Cheng 		return err;
9783a6ba7dcSVincent Cheng 
9793a6ba7dcSVincent Cheng 	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
9803a6ba7dcSVincent Cheng 	if (err)
9813a6ba7dcSVincent Cheng 		return err;
9823a6ba7dcSVincent Cheng 
9833a6ba7dcSVincent Cheng 	err = idtcm_start_phase_pull_in(channel);
9843a6ba7dcSVincent Cheng 
9853a6ba7dcSVincent Cheng 	return err;
9863a6ba7dcSVincent Cheng }
9873a6ba7dcSVincent Cheng 
set_tod_write_overhead(struct idtcm_channel * channel)9887ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel)
9897ea5fda2SMin Li {
9907ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
9917ea5fda2SMin Li 	s64 current_ns = 0;
9927ea5fda2SMin Li 	s64 lowest_ns = 0;
9937ea5fda2SMin Li 	int err;
9947ea5fda2SMin Li 	u8 i;
9957ea5fda2SMin Li 	ktime_t start;
9967ea5fda2SMin Li 	ktime_t stop;
9977260d1c8SMin Li 	ktime_t diff;
9987ea5fda2SMin Li 
9997ea5fda2SMin Li 	char buf[TOD_BYTE_COUNT] = {0};
10007ea5fda2SMin Li 
10017ea5fda2SMin Li 	/* Set page offset */
10027ea5fda2SMin Li 	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
10037ea5fda2SMin Li 		    buf, sizeof(buf));
10047ea5fda2SMin Li 
10057ea5fda2SMin Li 	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
10067ea5fda2SMin Li 		start = ktime_get_raw();
10077ea5fda2SMin Li 
10087ea5fda2SMin Li 		err = idtcm_write(idtcm, channel->hw_dpll_n,
10097ea5fda2SMin Li 				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
10107ea5fda2SMin Li 		if (err)
10117ea5fda2SMin Li 			return err;
10127ea5fda2SMin Li 
10137ea5fda2SMin Li 		stop = ktime_get_raw();
10147ea5fda2SMin Li 
10157260d1c8SMin Li 		diff = ktime_sub(stop, start);
10167260d1c8SMin Li 
10177260d1c8SMin Li 		current_ns = ktime_to_ns(diff);
10187ea5fda2SMin Li 
10197ea5fda2SMin Li 		if (i == 0) {
10207ea5fda2SMin Li 			lowest_ns = current_ns;
10217ea5fda2SMin Li 		} else {
10227ea5fda2SMin Li 			if (current_ns < lowest_ns)
10237ea5fda2SMin Li 				lowest_ns = current_ns;
10247ea5fda2SMin Li 		}
10257ea5fda2SMin Li 	}
10267ea5fda2SMin Li 
10277ea5fda2SMin Li 	idtcm->tod_write_overhead_ns = lowest_ns;
10287ea5fda2SMin Li 
10297ea5fda2SMin Li 	return err;
10307ea5fda2SMin Li }
10317ea5fda2SMin Li 
_idtcm_adjtime_deprecated(struct idtcm_channel * channel,s64 delta)1032da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
10333a6ba7dcSVincent Cheng {
10343a6ba7dcSVincent Cheng 	int err;
10353a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
10363a6ba7dcSVincent Cheng 	struct timespec64 ts;
10373a6ba7dcSVincent Cheng 	s64 now;
10383a6ba7dcSVincent Cheng 
1039da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
1040da9facf1SMin Li 		err = channel->do_phase_pull_in(channel, delta, 0);
10413a6ba7dcSVincent Cheng 	} else {
10423a6ba7dcSVincent Cheng 		idtcm->calculate_overhead_flag = 1;
10433a6ba7dcSVincent Cheng 
10447ea5fda2SMin Li 		err = set_tod_write_overhead(channel);
10457ea5fda2SMin Li 		if (err)
10467ea5fda2SMin Li 			return err;
10477ea5fda2SMin Li 
1048930dfa56SMin Li 		err = _idtcm_gettime_immediate(channel, &ts);
10493a6ba7dcSVincent Cheng 		if (err)
10503a6ba7dcSVincent Cheng 			return err;
10513a6ba7dcSVincent Cheng 
10523a6ba7dcSVincent Cheng 		now = timespec64_to_ns(&ts);
10533a6ba7dcSVincent Cheng 		now += delta;
10543a6ba7dcSVincent Cheng 
10553a6ba7dcSVincent Cheng 		ts = ns_to_timespec64(now);
10563a6ba7dcSVincent Cheng 
1057da948233SMin Li 		err = _idtcm_settime_deprecated(channel, &ts);
10583a6ba7dcSVincent Cheng 	}
10593a6ba7dcSVincent Cheng 
10603a6ba7dcSVincent Cheng 	return err;
10613a6ba7dcSVincent Cheng }
10623a6ba7dcSVincent Cheng 
idtcm_state_machine_reset(struct idtcm * idtcm)10633a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm)
10643a6ba7dcSVincent Cheng {
10653a6ba7dcSVincent Cheng 	u8 byte = SM_RESET_CMD;
1066251f4fe2SMin Li 	u32 status = 0;
1067251f4fe2SMin Li 	int err;
1068251f4fe2SMin Li 	u8 i;
1069251f4fe2SMin Li 
1070251f4fe2SMin Li 	clear_boot_status(idtcm);
10713a6ba7dcSVincent Cheng 
1072794c3dffSMin Li 	err = idtcm_write(idtcm, RESET_CTRL,
1073794c3dffSMin Li 			  IDTCM_FW_REG(idtcm->fw_ver, V520, SM_RESET),
1074794c3dffSMin Li 			  &byte, sizeof(byte));
10753a6ba7dcSVincent Cheng 
1076251f4fe2SMin Li 	if (!err) {
1077251f4fe2SMin Li 		for (i = 0; i < 30; i++) {
1078251f4fe2SMin Li 			msleep_interruptible(100);
1079251f4fe2SMin Li 			read_boot_status(idtcm, &status);
1080251f4fe2SMin Li 
1081251f4fe2SMin Li 			if (status == 0xA0) {
1082930dfa56SMin Li 				dev_dbg(idtcm->dev,
10831c49d3e9SVincent Cheng 					"SM_RESET completed in %d ms", i * 100);
1084251f4fe2SMin Li 				break;
1085251f4fe2SMin Li 			}
1086251f4fe2SMin Li 		}
1087251f4fe2SMin Li 
1088251f4fe2SMin Li 		if (!status)
1089930dfa56SMin Li 			dev_err(idtcm->dev,
10901c49d3e9SVincent Cheng 				"Timed out waiting for CM_RESET to complete");
1091251f4fe2SMin Li 	}
10923a6ba7dcSVincent Cheng 
10933a6ba7dcSVincent Cheng 	return err;
10943a6ba7dcSVincent Cheng }
10953a6ba7dcSVincent Cheng 
idtcm_read_hw_rev_id(struct idtcm * idtcm,u8 * hw_rev_id)10963a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
10973a6ba7dcSVincent Cheng {
10981ece2fbeSVincent Cheng 	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
10993a6ba7dcSVincent Cheng }
11003a6ba7dcSVincent Cheng 
idtcm_read_product_id(struct idtcm * idtcm,u16 * product_id)11013a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
11023a6ba7dcSVincent Cheng {
11033a6ba7dcSVincent Cheng 	int err;
11043a6ba7dcSVincent Cheng 	u8 buf[2] = {0};
11053a6ba7dcSVincent Cheng 
11063a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
11073a6ba7dcSVincent Cheng 
11083a6ba7dcSVincent Cheng 	*product_id = (buf[1] << 8) | buf[0];
11093a6ba7dcSVincent Cheng 
11103a6ba7dcSVincent Cheng 	return err;
11113a6ba7dcSVincent Cheng }
11123a6ba7dcSVincent Cheng 
idtcm_read_major_release(struct idtcm * idtcm,u8 * major)11133a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
11143a6ba7dcSVincent Cheng {
11153a6ba7dcSVincent Cheng 	int err;
11163a6ba7dcSVincent Cheng 	u8 buf = 0;
11173a6ba7dcSVincent Cheng 
11183a6ba7dcSVincent Cheng 	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
11193a6ba7dcSVincent Cheng 
11203a6ba7dcSVincent Cheng 	*major = buf >> 1;
11213a6ba7dcSVincent Cheng 
11223a6ba7dcSVincent Cheng 	return err;
11233a6ba7dcSVincent Cheng }
11243a6ba7dcSVincent Cheng 
idtcm_read_minor_release(struct idtcm * idtcm,u8 * minor)11253a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
11263a6ba7dcSVincent Cheng {
11273a6ba7dcSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
11283a6ba7dcSVincent Cheng }
11293a6ba7dcSVincent Cheng 
idtcm_read_hotfix_release(struct idtcm * idtcm,u8 * hotfix)11303a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
11313a6ba7dcSVincent Cheng {
11323a6ba7dcSVincent Cheng 	return idtcm_read(idtcm,
11333a6ba7dcSVincent Cheng 			  GENERAL_STATUS,
11343a6ba7dcSVincent Cheng 			  HOTFIX_REL,
11353a6ba7dcSVincent Cheng 			  hotfix,
11363a6ba7dcSVincent Cheng 			  sizeof(u8));
11373a6ba7dcSVincent Cheng }
11383a6ba7dcSVincent Cheng 
idtcm_read_otp_scsr_config_select(struct idtcm * idtcm,u8 * config_select)11391ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
11401ece2fbeSVincent Cheng 					     u8 *config_select)
11413a6ba7dcSVincent Cheng {
11421ece2fbeSVincent Cheng 	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
11431ece2fbeSVincent Cheng 			  config_select, sizeof(u8));
11443a6ba7dcSVincent Cheng }
11453a6ba7dcSVincent Cheng 
set_pll_output_mask(struct idtcm * idtcm,u16 addr,u8 val)11463a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
11473a6ba7dcSVincent Cheng {
11483a6ba7dcSVincent Cheng 	int err = 0;
11493a6ba7dcSVincent Cheng 
11503a6ba7dcSVincent Cheng 	switch (addr) {
11517ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR:
11523a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[0].output_mask, val);
11533a6ba7dcSVincent Cheng 		break;
11547ea5fda2SMin Li 	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
11553a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[0].output_mask, val);
11563a6ba7dcSVincent Cheng 		break;
11577ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR:
11583a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[1].output_mask, val);
11593a6ba7dcSVincent Cheng 		break;
11607ea5fda2SMin Li 	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
11613a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[1].output_mask, val);
11623a6ba7dcSVincent Cheng 		break;
11637ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR:
11643a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[2].output_mask, val);
11653a6ba7dcSVincent Cheng 		break;
11667ea5fda2SMin Li 	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
11673a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[2].output_mask, val);
11683a6ba7dcSVincent Cheng 		break;
11697ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR:
11703a6ba7dcSVincent Cheng 		SET_U16_LSB(idtcm->channel[3].output_mask, val);
11713a6ba7dcSVincent Cheng 		break;
11727ea5fda2SMin Li 	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
11733a6ba7dcSVincent Cheng 		SET_U16_MSB(idtcm->channel[3].output_mask, val);
11743a6ba7dcSVincent Cheng 		break;
11753a6ba7dcSVincent Cheng 	default:
11767ea5fda2SMin Li 		err = -EFAULT; /* Bad address */;
11773a6ba7dcSVincent Cheng 		break;
11783a6ba7dcSVincent Cheng 	}
11793a6ba7dcSVincent Cheng 
11803a6ba7dcSVincent Cheng 	return err;
11813a6ba7dcSVincent Cheng }
11823a6ba7dcSVincent Cheng 
set_tod_ptp_pll(struct idtcm * idtcm,u8 index,u8 pll)11837ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
11847ea5fda2SMin Li {
11857ea5fda2SMin Li 	if (index >= MAX_TOD) {
1186930dfa56SMin Li 		dev_err(idtcm->dev, "ToD%d not supported", index);
11877ea5fda2SMin Li 		return -EINVAL;
11887ea5fda2SMin Li 	}
11897ea5fda2SMin Li 
11907ea5fda2SMin Li 	if (pll >= MAX_PLL) {
1191930dfa56SMin Li 		dev_err(idtcm->dev, "Pll%d not supported", pll);
11927ea5fda2SMin Li 		return -EINVAL;
11937ea5fda2SMin Li 	}
11947ea5fda2SMin Li 
11957ea5fda2SMin Li 	idtcm->channel[index].pll = pll;
11967ea5fda2SMin Li 
11977ea5fda2SMin Li 	return 0;
11987ea5fda2SMin Li }
11997ea5fda2SMin Li 
check_and_set_masks(struct idtcm * idtcm,u16 regaddr,u8 val)12003a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm,
12013a6ba7dcSVincent Cheng 			       u16 regaddr,
12023a6ba7dcSVincent Cheng 			       u8 val)
12033a6ba7dcSVincent Cheng {
12043a6ba7dcSVincent Cheng 	int err = 0;
12053a6ba7dcSVincent Cheng 
12067ea5fda2SMin Li 	switch (regaddr) {
12077ea5fda2SMin Li 	case TOD_MASK_ADDR:
12087ea5fda2SMin Li 		if ((val & 0xf0) || !(val & 0x0f)) {
1209930dfa56SMin Li 			dev_err(idtcm->dev, "Invalid TOD mask 0x%02x", val);
12107ea5fda2SMin Li 			err = -EINVAL;
12117ea5fda2SMin Li 		} else {
12127ea5fda2SMin Li 			idtcm->tod_mask = val;
12137ea5fda2SMin Li 		}
12147ea5fda2SMin Li 		break;
12157ea5fda2SMin Li 	case TOD0_PTP_PLL_ADDR:
12167ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 0, val);
12177ea5fda2SMin Li 		break;
12187ea5fda2SMin Li 	case TOD1_PTP_PLL_ADDR:
12197ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 1, val);
12207ea5fda2SMin Li 		break;
12217ea5fda2SMin Li 	case TOD2_PTP_PLL_ADDR:
12227ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 2, val);
12237ea5fda2SMin Li 		break;
12247ea5fda2SMin Li 	case TOD3_PTP_PLL_ADDR:
12257ea5fda2SMin Li 		err = set_tod_ptp_pll(idtcm, 3, val);
12267ea5fda2SMin Li 		break;
12277ea5fda2SMin Li 	default:
12287ea5fda2SMin Li 		err = set_pll_output_mask(idtcm, regaddr, val);
12297ea5fda2SMin Li 		break;
12303a6ba7dcSVincent Cheng 	}
12313a6ba7dcSVincent Cheng 
12323a6ba7dcSVincent Cheng 	return err;
12333a6ba7dcSVincent Cheng }
12343a6ba7dcSVincent Cheng 
display_pll_and_masks(struct idtcm * idtcm)12357ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm)
12363a6ba7dcSVincent Cheng {
12373a6ba7dcSVincent Cheng 	u8 i;
12383a6ba7dcSVincent Cheng 	u8 mask;
12393a6ba7dcSVincent Cheng 
1240930dfa56SMin Li 	dev_dbg(idtcm->dev, "tod_mask = 0x%02x", idtcm->tod_mask);
12413a6ba7dcSVincent Cheng 
12427ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
12433a6ba7dcSVincent Cheng 		mask = 1 << i;
12443a6ba7dcSVincent Cheng 
12457ea5fda2SMin Li 		if (mask & idtcm->tod_mask)
1246930dfa56SMin Li 			dev_dbg(idtcm->dev,
12471c49d3e9SVincent Cheng 				"TOD%d pll = %d    output_mask = 0x%04x",
12487ea5fda2SMin Li 				i, idtcm->channel[i].pll,
12497ea5fda2SMin Li 				idtcm->channel[i].output_mask);
12503a6ba7dcSVincent Cheng 	}
12513a6ba7dcSVincent Cheng }
12523a6ba7dcSVincent Cheng 
idtcm_load_firmware(struct idtcm * idtcm,struct device * dev)12533a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm,
12543a6ba7dcSVincent Cheng 			       struct device *dev)
12553a6ba7dcSVincent Cheng {
1256794c3dffSMin Li 	u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH);
12577ea5fda2SMin Li 	char fname[128] = FW_FILENAME;
12583a6ba7dcSVincent Cheng 	const struct firmware *fw;
12593a6ba7dcSVincent Cheng 	struct idtcm_fwrc *rec;
12603a6ba7dcSVincent Cheng 	u32 regaddr;
12613a6ba7dcSVincent Cheng 	int err;
12623a6ba7dcSVincent Cheng 	s32 len;
12633a6ba7dcSVincent Cheng 	u8 val;
12643a6ba7dcSVincent Cheng 	u8 loaddr;
12653a6ba7dcSVincent Cheng 
12667ea5fda2SMin Li 	if (firmware) /* module parameter */
12677ea5fda2SMin Li 		snprintf(fname, sizeof(fname), "%s", firmware);
12683a6ba7dcSVincent Cheng 
1269930dfa56SMin Li 	dev_info(idtcm->dev, "requesting firmware '%s'", fname);
12703a6ba7dcSVincent Cheng 
12717ea5fda2SMin Li 	err = request_firmware(&fw, fname, dev);
12727ea5fda2SMin Li 	if (err) {
1273930dfa56SMin Li 		dev_err(idtcm->dev,
12741c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
12753a6ba7dcSVincent Cheng 		return err;
12767ea5fda2SMin Li 	}
12773a6ba7dcSVincent Cheng 
1278930dfa56SMin Li 	dev_dbg(idtcm->dev, "firmware size %zu bytes", fw->size);
12793a6ba7dcSVincent Cheng 
12803a6ba7dcSVincent Cheng 	rec = (struct idtcm_fwrc *) fw->data;
12813a6ba7dcSVincent Cheng 
1282794c3dffSMin Li 	if (contains_full_configuration(idtcm, fw))
12833a6ba7dcSVincent Cheng 		idtcm_state_machine_reset(idtcm);
12843a6ba7dcSVincent Cheng 
12853a6ba7dcSVincent Cheng 	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
12863a6ba7dcSVincent Cheng 		if (rec->reserved) {
1287930dfa56SMin Li 			dev_err(idtcm->dev,
12881c49d3e9SVincent Cheng 				"bad firmware, reserved field non-zero");
12893a6ba7dcSVincent Cheng 			err = -EINVAL;
12903a6ba7dcSVincent Cheng 		} else {
12913a6ba7dcSVincent Cheng 			regaddr = rec->hiaddr << 8;
12923a6ba7dcSVincent Cheng 			regaddr |= rec->loaddr;
12933a6ba7dcSVincent Cheng 
12943a6ba7dcSVincent Cheng 			val = rec->value;
12953a6ba7dcSVincent Cheng 			loaddr = rec->loaddr;
12963a6ba7dcSVincent Cheng 
12973a6ba7dcSVincent Cheng 			rec++;
12983a6ba7dcSVincent Cheng 
12993a6ba7dcSVincent Cheng 			err = check_and_set_masks(idtcm, regaddr, val);
13003a6ba7dcSVincent Cheng 		}
13013a6ba7dcSVincent Cheng 
13027ea5fda2SMin Li 		if (err != -EINVAL) {
13037ea5fda2SMin Li 			err = 0;
13047ea5fda2SMin Li 
13053a6ba7dcSVincent Cheng 			/* Top (status registers) and bottom are read-only */
1306794c3dffSMin Li 			if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch)
13073a6ba7dcSVincent Cheng 				continue;
13083a6ba7dcSVincent Cheng 
13093a6ba7dcSVincent Cheng 			/* Page size 128, last 4 bytes of page skipped */
131077fdb168SVincent Cheng 			if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
13113a6ba7dcSVincent Cheng 				continue;
13123a6ba7dcSVincent Cheng 
13133a6ba7dcSVincent Cheng 			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
13143a6ba7dcSVincent Cheng 		}
13153a6ba7dcSVincent Cheng 
13163a6ba7dcSVincent Cheng 		if (err)
13173a6ba7dcSVincent Cheng 			goto out;
13183a6ba7dcSVincent Cheng 	}
13193a6ba7dcSVincent Cheng 
13207ea5fda2SMin Li 	display_pll_and_masks(idtcm);
13213a6ba7dcSVincent Cheng 
13223a6ba7dcSVincent Cheng out:
13233a6ba7dcSVincent Cheng 	release_firmware(fw);
13243a6ba7dcSVincent Cheng 	return err;
13253a6ba7dcSVincent Cheng }
13263a6ba7dcSVincent Cheng 
idtcm_output_enable(struct idtcm_channel * channel,bool enable,unsigned int outn)13277ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel,
13287ea5fda2SMin Li 			       bool enable, unsigned int outn)
13293a6ba7dcSVincent Cheng {
13303a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13317260d1c8SMin Li 	int base;
13323a6ba7dcSVincent Cheng 	int err;
13337ea5fda2SMin Li 	u8 val;
13343a6ba7dcSVincent Cheng 
1335794c3dffSMin Li 	base = get_output_base_addr(idtcm->fw_ver, outn);
13367260d1c8SMin Li 
13377260d1c8SMin Li 	if (!(base > 0)) {
1338930dfa56SMin Li 		dev_err(idtcm->dev,
13397260d1c8SMin Li 			"%s - Unsupported out%d", __func__, outn);
13407260d1c8SMin Li 		return base;
13417260d1c8SMin Li 	}
13427260d1c8SMin Li 
13437260d1c8SMin Li 	err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13443a6ba7dcSVincent Cheng 	if (err)
13453a6ba7dcSVincent Cheng 		return err;
13463a6ba7dcSVincent Cheng 
13473a6ba7dcSVincent Cheng 	if (enable)
13483a6ba7dcSVincent Cheng 		val |= SQUELCH_DISABLE;
13493a6ba7dcSVincent Cheng 	else
13503a6ba7dcSVincent Cheng 		val &= ~SQUELCH_DISABLE;
13513a6ba7dcSVincent Cheng 
13527260d1c8SMin Li 	return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
13537ea5fda2SMin Li }
13547ea5fda2SMin Li 
idtcm_perout_enable(struct idtcm_channel * channel,struct ptp_perout_request * perout,bool enable)13557ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel,
1356930dfa56SMin Li 			       struct ptp_perout_request *perout,
1357930dfa56SMin Li 			       bool enable)
13587ea5fda2SMin Li {
1359e8b4d8b5SVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1360e8b4d8b5SVincent Cheng 	struct timespec64 ts = {0, 0};
1361e8b4d8b5SVincent Cheng 	int err;
13627ea5fda2SMin Li 
1363e8b4d8b5SVincent Cheng 	err = idtcm_output_enable(channel, enable, perout->index);
13647ea5fda2SMin Li 
1365e8b4d8b5SVincent Cheng 	if (err) {
1366930dfa56SMin Li 		dev_err(idtcm->dev, "Unable to set output enable");
1367e8b4d8b5SVincent Cheng 		return err;
1368e8b4d8b5SVincent Cheng 	}
1369e8b4d8b5SVincent Cheng 
1370e8b4d8b5SVincent Cheng 	/* Align output to internal 1 PPS */
1371e8b4d8b5SVincent Cheng 	return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS);
13727ea5fda2SMin Li }
13737ea5fda2SMin Li 
idtcm_get_pll_mode(struct idtcm_channel * channel,enum pll_mode * mode)13747260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel,
1375da9facf1SMin Li 			      enum pll_mode *mode)
13767260d1c8SMin Li {
13777260d1c8SMin Li 	struct idtcm *idtcm = channel->idtcm;
13787260d1c8SMin Li 	int err;
13797260d1c8SMin Li 	u8 dpll_mode;
13807260d1c8SMin Li 
1381794c3dffSMin Li 	err = idtcm_read(idtcm, channel->dpll_n,
1382794c3dffSMin Li 			 IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
13837260d1c8SMin Li 			 &dpll_mode, sizeof(dpll_mode));
13847260d1c8SMin Li 	if (err)
13857260d1c8SMin Li 		return err;
13867260d1c8SMin Li 
1387da9facf1SMin Li 	*mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
13887260d1c8SMin Li 
13897260d1c8SMin Li 	return 0;
13907260d1c8SMin Li }
13917260d1c8SMin Li 
idtcm_set_pll_mode(struct idtcm_channel * channel,enum pll_mode mode)13923a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel,
1393da9facf1SMin Li 			      enum pll_mode mode)
13943a6ba7dcSVincent Cheng {
13953a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
13963a6ba7dcSVincent Cheng 	int err;
13973a6ba7dcSVincent Cheng 	u8 dpll_mode;
13983a6ba7dcSVincent Cheng 
1399794c3dffSMin Li 	err = idtcm_read(idtcm, channel->dpll_n,
1400794c3dffSMin Li 			 IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
14013a6ba7dcSVincent Cheng 			 &dpll_mode, sizeof(dpll_mode));
14023a6ba7dcSVincent Cheng 	if (err)
14033a6ba7dcSVincent Cheng 		return err;
14043a6ba7dcSVincent Cheng 
14053a6ba7dcSVincent Cheng 	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
14063a6ba7dcSVincent Cheng 
1407da9facf1SMin Li 	dpll_mode |= (mode << PLL_MODE_SHIFT);
14083a6ba7dcSVincent Cheng 
1409794c3dffSMin Li 	err = idtcm_write(idtcm, channel->dpll_n,
1410794c3dffSMin Li 			  IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
14113a6ba7dcSVincent Cheng 			  &dpll_mode, sizeof(dpll_mode));
1412da9facf1SMin Li 	return err;
1413da9facf1SMin Li }
1414da9facf1SMin Li 
idtcm_get_manual_reference(struct idtcm_channel * channel,enum manual_reference * ref)1415da9facf1SMin Li static int idtcm_get_manual_reference(struct idtcm_channel *channel,
1416da9facf1SMin Li 				      enum manual_reference *ref)
1417da9facf1SMin Li {
1418da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1419da9facf1SMin Li 	u8 dpll_manu_ref_cfg;
1420da9facf1SMin Li 	int err;
1421da9facf1SMin Li 
1422da9facf1SMin Li 	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
1423da9facf1SMin Li 			 DPLL_CTRL_DPLL_MANU_REF_CFG,
1424da9facf1SMin Li 			 &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
14253a6ba7dcSVincent Cheng 	if (err)
14263a6ba7dcSVincent Cheng 		return err;
14273a6ba7dcSVincent Cheng 
1428da9facf1SMin Li 	dpll_manu_ref_cfg &= (MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT);
1429da9facf1SMin Li 
1430da9facf1SMin Li 	*ref = dpll_manu_ref_cfg >> MANUAL_REFERENCE_SHIFT;
1431da9facf1SMin Li 
14323a6ba7dcSVincent Cheng 	return 0;
14333a6ba7dcSVincent Cheng }
14343a6ba7dcSVincent Cheng 
idtcm_set_manual_reference(struct idtcm_channel * channel,enum manual_reference ref)1435da9facf1SMin Li static int idtcm_set_manual_reference(struct idtcm_channel *channel,
1436da9facf1SMin Li 				      enum manual_reference ref)
1437da9facf1SMin Li {
1438da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1439da9facf1SMin Li 	u8 dpll_manu_ref_cfg;
1440da9facf1SMin Li 	int err;
1441da9facf1SMin Li 
1442da9facf1SMin Li 	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
1443da9facf1SMin Li 			 DPLL_CTRL_DPLL_MANU_REF_CFG,
1444da9facf1SMin Li 			 &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
1445da9facf1SMin Li 	if (err)
1446da9facf1SMin Li 		return err;
1447da9facf1SMin Li 
1448da9facf1SMin Li 	dpll_manu_ref_cfg &= ~(MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT);
1449da9facf1SMin Li 
1450da9facf1SMin Li 	dpll_manu_ref_cfg |= (ref << MANUAL_REFERENCE_SHIFT);
1451da9facf1SMin Li 
1452da9facf1SMin Li 	err = idtcm_write(idtcm, channel->dpll_ctrl_n,
1453da9facf1SMin Li 			  DPLL_CTRL_DPLL_MANU_REF_CFG,
1454da9facf1SMin Li 			  &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
1455da9facf1SMin Li 
1456da9facf1SMin Li 	return err;
1457da9facf1SMin Li }
1458da9facf1SMin Li 
configure_dpll_mode_write_frequency(struct idtcm_channel * channel)1459da9facf1SMin Li static int configure_dpll_mode_write_frequency(struct idtcm_channel *channel)
1460da9facf1SMin Li {
1461da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1462da9facf1SMin Li 	int err;
1463da9facf1SMin Li 
1464da9facf1SMin Li 	err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
1465da9facf1SMin Li 
1466da9facf1SMin Li 	if (err)
1467930dfa56SMin Li 		dev_err(idtcm->dev, "Failed to set pll mode to write frequency");
1468da9facf1SMin Li 	else
1469da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
1470da9facf1SMin Li 
1471da9facf1SMin Li 	return err;
1472da9facf1SMin Li }
1473da9facf1SMin Li 
configure_dpll_mode_write_phase(struct idtcm_channel * channel)1474da9facf1SMin Li static int configure_dpll_mode_write_phase(struct idtcm_channel *channel)
1475da9facf1SMin Li {
1476da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1477da9facf1SMin Li 	int err;
1478da9facf1SMin Li 
1479da9facf1SMin Li 	err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
1480da9facf1SMin Li 
1481da9facf1SMin Li 	if (err)
1482930dfa56SMin Li 		dev_err(idtcm->dev, "Failed to set pll mode to write phase");
1483da9facf1SMin Li 	else
1484da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
1485da9facf1SMin Li 
1486da9facf1SMin Li 	return err;
1487da9facf1SMin Li }
1488da9facf1SMin Li 
configure_manual_reference_write_frequency(struct idtcm_channel * channel)1489da9facf1SMin Li static int configure_manual_reference_write_frequency(struct idtcm_channel *channel)
1490da9facf1SMin Li {
1491da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1492da9facf1SMin Li 	int err;
1493da9facf1SMin Li 
1494da9facf1SMin Li 	err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_FREQUENCY);
1495da9facf1SMin Li 
1496da9facf1SMin Li 	if (err)
1497930dfa56SMin Li 		dev_err(idtcm->dev, "Failed to set manual reference to write frequency");
1498da9facf1SMin Li 	else
1499da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
1500da9facf1SMin Li 
1501da9facf1SMin Li 	return err;
1502da9facf1SMin Li }
1503da9facf1SMin Li 
configure_manual_reference_write_phase(struct idtcm_channel * channel)1504da9facf1SMin Li static int configure_manual_reference_write_phase(struct idtcm_channel *channel)
1505da9facf1SMin Li {
1506da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1507da9facf1SMin Li 	int err;
1508da9facf1SMin Li 
1509da9facf1SMin Li 	err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_PHASE);
1510da9facf1SMin Li 
1511da9facf1SMin Li 	if (err)
1512930dfa56SMin Li 		dev_err(idtcm->dev, "Failed to set manual reference to write phase");
1513da9facf1SMin Li 	else
1514da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
1515da9facf1SMin Li 
1516da9facf1SMin Li 	return err;
1517da9facf1SMin Li }
1518da9facf1SMin Li 
idtcm_stop_phase_pull_in(struct idtcm_channel * channel)1519da9facf1SMin Li static int idtcm_stop_phase_pull_in(struct idtcm_channel *channel)
1520da9facf1SMin Li {
1521da9facf1SMin Li 	int err;
1522da9facf1SMin Li 
1523da9facf1SMin Li 	err = _idtcm_adjfine(channel, channel->current_freq_scaled_ppm);
1524da9facf1SMin Li 	if (err)
1525da9facf1SMin Li 		return err;
1526da9facf1SMin Li 
1527da9facf1SMin Li 	channel->phase_pull_in = false;
1528da9facf1SMin Li 
1529da9facf1SMin Li 	return 0;
1530da9facf1SMin Li }
1531da9facf1SMin Li 
idtcm_work_handler(struct ptp_clock_info * ptp)1532da9facf1SMin Li static long idtcm_work_handler(struct ptp_clock_info *ptp)
1533da9facf1SMin Li {
1534da9facf1SMin Li 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1535da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1536da9facf1SMin Li 
1537930dfa56SMin Li 	mutex_lock(idtcm->lock);
1538da9facf1SMin Li 
1539da9facf1SMin Li 	(void)idtcm_stop_phase_pull_in(channel);
1540da9facf1SMin Li 
1541930dfa56SMin Li 	mutex_unlock(idtcm->lock);
1542da9facf1SMin Li 
1543da9facf1SMin Li 	/* Return a negative value here to not reschedule */
1544da9facf1SMin Li 	return -1;
1545da9facf1SMin Li }
1546da9facf1SMin Li 
phase_pull_in_scaled_ppm(s32 current_ppm,s32 phase_pull_in_ppb)1547da9facf1SMin Li static s32 phase_pull_in_scaled_ppm(s32 current_ppm, s32 phase_pull_in_ppb)
1548da9facf1SMin Li {
1549da9facf1SMin Li 	/* ppb = scaled_ppm * 125 / 2^13 */
1550da9facf1SMin Li 	/* scaled_ppm = ppb * 2^13 / 125 */
1551da9facf1SMin Li 
1552930dfa56SMin Li 	s64 max_scaled_ppm = div_s64((s64)PHASE_PULL_IN_MAX_PPB << 13, 125);
1553930dfa56SMin Li 	s64 scaled_ppm = div_s64((s64)phase_pull_in_ppb << 13, 125);
1554da9facf1SMin Li 
1555da9facf1SMin Li 	current_ppm += scaled_ppm;
1556da9facf1SMin Li 
1557da9facf1SMin Li 	if (current_ppm > max_scaled_ppm)
1558da9facf1SMin Li 		current_ppm = max_scaled_ppm;
1559da9facf1SMin Li 	else if (current_ppm < -max_scaled_ppm)
1560da9facf1SMin Li 		current_ppm = -max_scaled_ppm;
1561da9facf1SMin Li 
1562da9facf1SMin Li 	return current_ppm;
1563da9facf1SMin Li }
1564da9facf1SMin Li 
do_phase_pull_in_sw(struct idtcm_channel * channel,s32 delta_ns,u32 max_ffo_ppb)1565da9facf1SMin Li static int do_phase_pull_in_sw(struct idtcm_channel *channel,
1566da9facf1SMin Li 			       s32 delta_ns,
1567da9facf1SMin Li 			       u32 max_ffo_ppb)
1568da9facf1SMin Li {
1569da9facf1SMin Li 	s32 current_ppm = channel->current_freq_scaled_ppm;
1570da9facf1SMin Li 	u32 duration_ms = MSEC_PER_SEC;
1571da9facf1SMin Li 	s32 delta_ppm;
1572da9facf1SMin Li 	s32 ppb;
1573da9facf1SMin Li 	int err;
1574da9facf1SMin Li 
1575da9facf1SMin Li 	/* If the ToD correction is less than PHASE_PULL_IN_MIN_THRESHOLD_NS,
1576da9facf1SMin Li 	 * skip. The error introduced by the ToD adjustment procedure would
1577da9facf1SMin Li 	 * be bigger than the required ToD correction
1578da9facf1SMin Li 	 */
1579da9facf1SMin Li 	if (abs(delta_ns) < PHASE_PULL_IN_MIN_THRESHOLD_NS)
1580da9facf1SMin Li 		return 0;
1581da9facf1SMin Li 
1582da9facf1SMin Li 	if (max_ffo_ppb == 0)
1583da9facf1SMin Li 		max_ffo_ppb = PHASE_PULL_IN_MAX_PPB;
1584da9facf1SMin Li 
1585da9facf1SMin Li 	/* For most cases, keep phase pull-in duration 1 second */
1586da9facf1SMin Li 	ppb = delta_ns;
1587da9facf1SMin Li 	while (abs(ppb) > max_ffo_ppb) {
1588da9facf1SMin Li 		duration_ms *= 2;
1589da9facf1SMin Li 		ppb /= 2;
1590da9facf1SMin Li 	}
1591da9facf1SMin Li 
1592da9facf1SMin Li 	delta_ppm = phase_pull_in_scaled_ppm(current_ppm, ppb);
1593da9facf1SMin Li 
1594da9facf1SMin Li 	err = _idtcm_adjfine(channel, delta_ppm);
1595da9facf1SMin Li 
1596da9facf1SMin Li 	if (err)
1597da9facf1SMin Li 		return err;
1598da9facf1SMin Li 
1599da9facf1SMin Li 	/* schedule the worker to cancel phase pull-in */
1600da9facf1SMin Li 	ptp_schedule_worker(channel->ptp_clock,
1601da9facf1SMin Li 			    msecs_to_jiffies(duration_ms) - 1);
1602da9facf1SMin Li 
1603da9facf1SMin Li 	channel->phase_pull_in = true;
1604da9facf1SMin Li 
1605da9facf1SMin Li 	return 0;
1606da9facf1SMin Li }
1607da9facf1SMin Li 
initialize_operating_mode_with_manual_reference(struct idtcm_channel * channel,enum manual_reference ref)1608da9facf1SMin Li static int initialize_operating_mode_with_manual_reference(struct idtcm_channel *channel,
1609da9facf1SMin Li 							   enum manual_reference ref)
1610da9facf1SMin Li {
1611da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1612da9facf1SMin Li 
1613da9facf1SMin Li 	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
1614da9facf1SMin Li 	channel->configure_write_frequency = configure_manual_reference_write_frequency;
1615da9facf1SMin Li 	channel->configure_write_phase = configure_manual_reference_write_phase;
1616da9facf1SMin Li 	channel->do_phase_pull_in = do_phase_pull_in_sw;
1617da9facf1SMin Li 
1618da9facf1SMin Li 	switch (ref) {
1619da9facf1SMin Li 	case MANU_REF_WRITE_PHASE:
1620da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
1621da9facf1SMin Li 		break;
1622da9facf1SMin Li 	case MANU_REF_WRITE_FREQUENCY:
1623da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
1624da9facf1SMin Li 		break;
1625da9facf1SMin Li 	default:
1626930dfa56SMin Li 		dev_warn(idtcm->dev,
1627da9facf1SMin Li 			 "Unsupported MANUAL_REFERENCE: 0x%02x", ref);
1628da9facf1SMin Li 	}
1629da9facf1SMin Li 
1630da9facf1SMin Li 	return 0;
1631da9facf1SMin Li }
1632da9facf1SMin Li 
initialize_operating_mode_with_pll_mode(struct idtcm_channel * channel,enum pll_mode mode)1633da9facf1SMin Li static int initialize_operating_mode_with_pll_mode(struct idtcm_channel *channel,
1634da9facf1SMin Li 						   enum pll_mode mode)
1635da9facf1SMin Li {
1636da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1637da9facf1SMin Li 	int err = 0;
1638da9facf1SMin Li 
1639da9facf1SMin Li 	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
1640da9facf1SMin Li 	channel->configure_write_frequency = configure_dpll_mode_write_frequency;
1641da9facf1SMin Li 	channel->configure_write_phase = configure_dpll_mode_write_phase;
1642da9facf1SMin Li 	channel->do_phase_pull_in = do_phase_pull_in_fw;
1643da9facf1SMin Li 
1644da9facf1SMin Li 	switch (mode) {
1645da9facf1SMin Li 	case  PLL_MODE_WRITE_PHASE:
1646da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
1647da9facf1SMin Li 		break;
1648da9facf1SMin Li 	case PLL_MODE_WRITE_FREQUENCY:
1649da9facf1SMin Li 		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
1650da9facf1SMin Li 		break;
1651da9facf1SMin Li 	default:
1652930dfa56SMin Li 		dev_err(idtcm->dev,
1653da9facf1SMin Li 			"Unsupported PLL_MODE: 0x%02x", mode);
1654da9facf1SMin Li 		err = -EINVAL;
1655da9facf1SMin Li 	}
1656da9facf1SMin Li 
1657da9facf1SMin Li 	return err;
1658da9facf1SMin Li }
1659da9facf1SMin Li 
initialize_dco_operating_mode(struct idtcm_channel * channel)1660da9facf1SMin Li static int initialize_dco_operating_mode(struct idtcm_channel *channel)
1661da9facf1SMin Li {
1662da9facf1SMin Li 	enum manual_reference ref = MANU_REF_XO_DPLL;
1663da9facf1SMin Li 	enum pll_mode mode = PLL_MODE_DISABLED;
1664da9facf1SMin Li 	struct idtcm *idtcm = channel->idtcm;
1665da9facf1SMin Li 	int err;
1666da9facf1SMin Li 
1667da9facf1SMin Li 	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
1668da9facf1SMin Li 
1669da9facf1SMin Li 	err = idtcm_get_pll_mode(channel, &mode);
1670da9facf1SMin Li 	if (err) {
1671930dfa56SMin Li 		dev_err(idtcm->dev, "Unable to read pll mode!");
1672da9facf1SMin Li 		return err;
1673da9facf1SMin Li 	}
1674da9facf1SMin Li 
1675da9facf1SMin Li 	if (mode == PLL_MODE_PLL) {
1676da9facf1SMin Li 		err = idtcm_get_manual_reference(channel, &ref);
1677da9facf1SMin Li 		if (err) {
1678930dfa56SMin Li 			dev_err(idtcm->dev, "Unable to read manual reference!");
1679da9facf1SMin Li 			return err;
1680da9facf1SMin Li 		}
1681da9facf1SMin Li 		err = initialize_operating_mode_with_manual_reference(channel, ref);
1682da9facf1SMin Li 	} else {
1683da9facf1SMin Li 		err = initialize_operating_mode_with_pll_mode(channel, mode);
1684da9facf1SMin Li 	}
1685da9facf1SMin Li 
1686da9facf1SMin Li 	if (channel->mode == PTP_PLL_MODE_WRITE_PHASE)
1687da9facf1SMin Li 		channel->configure_write_frequency(channel);
1688da9facf1SMin Li 
1689da9facf1SMin Li 	return err;
1690da9facf1SMin Li }
1691da9facf1SMin Li 
16923a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */
16933a6ba7dcSVincent Cheng 
169487530779SRandy Dunlap /*
1695*c066e74fSRahul Rameshbabu  * Maximum absolute value for write phase offset in nanoseconds
1696bec67592SMin Li  *
1697425d2b1cSVincent Cheng  * Destination signed register is 32-bit register in resolution of 50ps
1698425d2b1cSVincent Cheng  *
1699*c066e74fSRahul Rameshbabu  * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350 ps
1700*c066e74fSRahul Rameshbabu  * Represent 107374182350 ps as 107374182 ns
1701*c066e74fSRahul Rameshbabu  */
idtcm_getmaxphase(struct ptp_clock_info * ptp __always_unused)1702*c066e74fSRahul Rameshbabu static s32 idtcm_getmaxphase(struct ptp_clock_info *ptp __always_unused)
1703*c066e74fSRahul Rameshbabu {
1704*c066e74fSRahul Rameshbabu 	return MAX_ABS_WRITE_PHASE_NANOSECONDS;
1705*c066e74fSRahul Rameshbabu }
1706*c066e74fSRahul Rameshbabu 
1707*c066e74fSRahul Rameshbabu /*
1708*c066e74fSRahul Rameshbabu  * Internal function for implementing support for write phase offset
1709*c066e74fSRahul Rameshbabu  *
1710*c066e74fSRahul Rameshbabu  * @channel:  channel
1711*c066e74fSRahul Rameshbabu  * @delta_ns: delta in nanoseconds
1712425d2b1cSVincent Cheng  */
_idtcm_adjphase(struct idtcm_channel * channel,s32 delta_ns)1713425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
1714425d2b1cSVincent Cheng {
1715425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1716425d2b1cSVincent Cheng 	int err;
1717425d2b1cSVincent Cheng 	u8 i;
1718425d2b1cSVincent Cheng 	u8 buf[4] = {0};
1719425d2b1cSVincent Cheng 	s32 phase_50ps;
1720425d2b1cSVincent Cheng 
1721da9facf1SMin Li 	if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) {
1722da9facf1SMin Li 		err = channel->configure_write_phase(channel);
1723425d2b1cSVincent Cheng 		if (err)
1724425d2b1cSVincent Cheng 			return err;
1725425d2b1cSVincent Cheng 	}
1726425d2b1cSVincent Cheng 
1727*c066e74fSRahul Rameshbabu 	phase_50ps = div_s64((s64)delta_ns * 1000, 50);
1728425d2b1cSVincent Cheng 
1729425d2b1cSVincent Cheng 	for (i = 0; i < 4; i++) {
1730425d2b1cSVincent Cheng 		buf[i] = phase_50ps & 0xff;
1731425d2b1cSVincent Cheng 		phase_50ps >>= 8;
1732425d2b1cSVincent Cheng 	}
1733425d2b1cSVincent Cheng 
1734425d2b1cSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
1735425d2b1cSVincent Cheng 			  buf, sizeof(buf));
1736425d2b1cSVincent Cheng 
1737425d2b1cSVincent Cheng 	return err;
1738425d2b1cSVincent Cheng }
1739425d2b1cSVincent Cheng 
_idtcm_adjfine(struct idtcm_channel * channel,long scaled_ppm)17407ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
17413a6ba7dcSVincent Cheng {
17423a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
17433a6ba7dcSVincent Cheng 	u8 i;
17443a6ba7dcSVincent Cheng 	int err;
17453a6ba7dcSVincent Cheng 	u8 buf[6] = {0};
17463a6ba7dcSVincent Cheng 	s64 fcw;
17473a6ba7dcSVincent Cheng 
1748da9facf1SMin Li 	if (channel->mode  != PTP_PLL_MODE_WRITE_FREQUENCY) {
1749da9facf1SMin Li 		err = channel->configure_write_frequency(channel);
17503a6ba7dcSVincent Cheng 		if (err)
17513a6ba7dcSVincent Cheng 			return err;
17523a6ba7dcSVincent Cheng 	}
17533a6ba7dcSVincent Cheng 
17543a6ba7dcSVincent Cheng 	/*
17553a6ba7dcSVincent Cheng 	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
17563a6ba7dcSVincent Cheng 	 *
17573a6ba7dcSVincent Cheng 	 * adjfreq:
17583a6ba7dcSVincent Cheng 	 *       ppb * 10^9
17593a6ba7dcSVincent Cheng 	 * FCW = ----------
17603a6ba7dcSVincent Cheng 	 *          111
17613a6ba7dcSVincent Cheng 	 *
17623a6ba7dcSVincent Cheng 	 * adjfine:
17633a6ba7dcSVincent Cheng 	 *       ppm_16 * 5^12
17643a6ba7dcSVincent Cheng 	 * FCW = -------------
17653a6ba7dcSVincent Cheng 	 *         111 * 2^4
17663a6ba7dcSVincent Cheng 	 */
17673a6ba7dcSVincent Cheng 
17683a6ba7dcSVincent Cheng 	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
17697ea5fda2SMin Li 	fcw = scaled_ppm * 244140625ULL;
17703a6ba7dcSVincent Cheng 
17717260d1c8SMin Li 	fcw = div_s64(fcw, 1776);
17723a6ba7dcSVincent Cheng 
17733a6ba7dcSVincent Cheng 	for (i = 0; i < 6; i++) {
17743a6ba7dcSVincent Cheng 		buf[i] = fcw & 0xff;
17753a6ba7dcSVincent Cheng 		fcw >>= 8;
17763a6ba7dcSVincent Cheng 	}
17773a6ba7dcSVincent Cheng 
17783a6ba7dcSVincent Cheng 	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
17793a6ba7dcSVincent Cheng 			  buf, sizeof(buf));
17803a6ba7dcSVincent Cheng 
17813a6ba7dcSVincent Cheng 	return err;
17823a6ba7dcSVincent Cheng }
17833a6ba7dcSVincent Cheng 
idtcm_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)17843a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
17853a6ba7dcSVincent Cheng {
1786fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
17873a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
17883a6ba7dcSVincent Cheng 	int err;
17893a6ba7dcSVincent Cheng 
1790930dfa56SMin Li 	mutex_lock(idtcm->lock);
1791930dfa56SMin Li 	err = _idtcm_gettime_immediate(channel, ts);
1792930dfa56SMin Li 	mutex_unlock(idtcm->lock);
17933a6ba7dcSVincent Cheng 
17947ea5fda2SMin Li 	if (err)
1795930dfa56SMin Li 		dev_err(idtcm->dev, "Failed at line %d in %s!",
17961c49d3e9SVincent Cheng 			__LINE__, __func__);
17977ea5fda2SMin Li 
17983a6ba7dcSVincent Cheng 	return err;
17993a6ba7dcSVincent Cheng }
18003a6ba7dcSVincent Cheng 
idtcm_settime_deprecated(struct ptp_clock_info * ptp,const struct timespec64 * ts)1801da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
18023a6ba7dcSVincent Cheng 				    const struct timespec64 *ts)
18033a6ba7dcSVincent Cheng {
1804fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
18053a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
18063a6ba7dcSVincent Cheng 	int err;
18073a6ba7dcSVincent Cheng 
1808930dfa56SMin Li 	mutex_lock(idtcm->lock);
1809da948233SMin Li 	err = _idtcm_settime_deprecated(channel, ts);
1810930dfa56SMin Li 	mutex_unlock(idtcm->lock);
18117ea5fda2SMin Li 
1812930dfa56SMin Li 	if (err)
1813930dfa56SMin Li 		dev_err(idtcm->dev,
1814930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
18157ea5fda2SMin Li 
18167ea5fda2SMin Li 	return err;
18177ea5fda2SMin Li }
18187ea5fda2SMin Li 
idtcm_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)1819da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp,
18207ea5fda2SMin Li 			 const struct timespec64 *ts)
18217ea5fda2SMin Li {
1822fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
18237ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
18247ea5fda2SMin Li 	int err;
18257ea5fda2SMin Li 
1826930dfa56SMin Li 	mutex_lock(idtcm->lock);
1827da948233SMin Li 	err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
1828930dfa56SMin Li 	mutex_unlock(idtcm->lock);
1829da948233SMin Li 
1830930dfa56SMin Li 	if (err)
1831930dfa56SMin Li 		dev_err(idtcm->dev,
1832930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
1833da948233SMin Li 
1834da948233SMin Li 	return err;
1835da948233SMin Li }
1836da948233SMin Li 
idtcm_adjtime_deprecated(struct ptp_clock_info * ptp,s64 delta)1837da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
1838da948233SMin Li {
1839fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1840da948233SMin Li 	struct idtcm *idtcm = channel->idtcm;
1841da948233SMin Li 	int err;
1842da948233SMin Li 
1843930dfa56SMin Li 	mutex_lock(idtcm->lock);
1844da948233SMin Li 	err = _idtcm_adjtime_deprecated(channel, delta);
1845930dfa56SMin Li 	mutex_unlock(idtcm->lock);
18467ea5fda2SMin Li 
1847930dfa56SMin Li 	if (err)
1848930dfa56SMin Li 		dev_err(idtcm->dev,
1849930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
18503a6ba7dcSVincent Cheng 
18513a6ba7dcSVincent Cheng 	return err;
18523a6ba7dcSVincent Cheng }
18533a6ba7dcSVincent Cheng 
idtcm_adjtime(struct ptp_clock_info * ptp,s64 delta)18543a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
18553a6ba7dcSVincent Cheng {
1856fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
18573a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
18587ea5fda2SMin Li 	struct timespec64 ts;
18597ea5fda2SMin Li 	enum scsr_tod_write_type_sel type;
18607ea5fda2SMin Li 	int err;
18617ea5fda2SMin Li 
1862da9facf1SMin Li 	if (channel->phase_pull_in == true)
18637c7dcd66SMin Li 		return -EBUSY;
1864da9facf1SMin Li 
1865930dfa56SMin Li 	mutex_lock(idtcm->lock);
1866da9facf1SMin Li 
1867da948233SMin Li 	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
1868da9facf1SMin Li 		err = channel->do_phase_pull_in(channel, delta, 0);
1869da9facf1SMin Li 	} else {
18707ea5fda2SMin Li 		if (delta >= 0) {
18717ea5fda2SMin Li 			ts = ns_to_timespec64(delta);
18727ea5fda2SMin Li 			type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
18737ea5fda2SMin Li 		} else {
18747ea5fda2SMin Li 			ts = ns_to_timespec64(-delta);
18757ea5fda2SMin Li 			type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
18767ea5fda2SMin Li 		}
1877da948233SMin Li 		err = _idtcm_settime(channel, &ts, type);
1878da9facf1SMin Li 	}
1879930dfa56SMin Li 
1880930dfa56SMin Li 	mutex_unlock(idtcm->lock);
1881930dfa56SMin Li 
1882930dfa56SMin Li 	if (err)
1883930dfa56SMin Li 		dev_err(idtcm->dev,
1884930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
18853a6ba7dcSVincent Cheng 
18863a6ba7dcSVincent Cheng 	return err;
18873a6ba7dcSVincent Cheng }
18883a6ba7dcSVincent Cheng 
idtcm_adjphase(struct ptp_clock_info * ptp,s32 delta)1889425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
1890425d2b1cSVincent Cheng {
1891fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1892425d2b1cSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
1893425d2b1cSVincent Cheng 	int err;
1894425d2b1cSVincent Cheng 
1895930dfa56SMin Li 	mutex_lock(idtcm->lock);
1896425d2b1cSVincent Cheng 	err = _idtcm_adjphase(channel, delta);
1897930dfa56SMin Li 	mutex_unlock(idtcm->lock);
18987ea5fda2SMin Li 
1899930dfa56SMin Li 	if (err)
1900930dfa56SMin Li 		dev_err(idtcm->dev,
1901930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
19027ea5fda2SMin Li 
19037ea5fda2SMin Li 	return err;
19047ea5fda2SMin Li }
19057ea5fda2SMin Li 
idtcm_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)19067ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
19077ea5fda2SMin Li {
1908fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
19097ea5fda2SMin Li 	struct idtcm *idtcm = channel->idtcm;
19107ea5fda2SMin Li 	int err;
19117ea5fda2SMin Li 
1912da9facf1SMin Li 	if (channel->phase_pull_in == true)
1913da9facf1SMin Li 		return 0;
1914da9facf1SMin Li 
1915da9facf1SMin Li 	if (scaled_ppm == channel->current_freq_scaled_ppm)
1916da9facf1SMin Li 		return 0;
1917da9facf1SMin Li 
1918930dfa56SMin Li 	mutex_lock(idtcm->lock);
19197ea5fda2SMin Li 	err = _idtcm_adjfine(channel, scaled_ppm);
1920930dfa56SMin Li 	mutex_unlock(idtcm->lock);
19217ea5fda2SMin Li 
1922930dfa56SMin Li 	if (err)
1923930dfa56SMin Li 		dev_err(idtcm->dev,
1924930dfa56SMin Li 			"Failed at line %d in %s!", __LINE__, __func__);
1925930dfa56SMin Li 	else
1926da9facf1SMin Li 		channel->current_freq_scaled_ppm = scaled_ppm;
1927da9facf1SMin Li 
1928425d2b1cSVincent Cheng 	return err;
1929425d2b1cSVincent Cheng }
1930425d2b1cSVincent Cheng 
idtcm_enable(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)19313a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp,
19323a6ba7dcSVincent Cheng 			struct ptp_clock_request *rq, int on)
19333a6ba7dcSVincent Cheng {
1934fcfd3757SVincent Cheng 	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
1935930dfa56SMin Li 	struct idtcm *idtcm = channel->idtcm;
1936930dfa56SMin Li 	int err = -EOPNOTSUPP;
1937930dfa56SMin Li 
1938930dfa56SMin Li 	mutex_lock(idtcm->lock);
19393a6ba7dcSVincent Cheng 
19403a6ba7dcSVincent Cheng 	switch (rq->type) {
19413a6ba7dcSVincent Cheng 	case PTP_CLK_REQ_PEROUT:
1942930dfa56SMin Li 		if (!on)
1943930dfa56SMin Li 			err = idtcm_perout_enable(channel, &rq->perout, false);
19443a6ba7dcSVincent Cheng 		/* Only accept a 1-PPS aligned to the second. */
1945930dfa56SMin Li 		else if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
19463a6ba7dcSVincent Cheng 			 rq->perout.period.nsec)
1947930dfa56SMin Li 			err = -ERANGE;
1948930dfa56SMin Li 		else
1949930dfa56SMin Li 			err = idtcm_perout_enable(channel, &rq->perout, true);
1950930dfa56SMin Li 		break;
1951930dfa56SMin Li 	case PTP_CLK_REQ_EXTTS:
1952bec67592SMin Li 		err = idtcm_extts_enable(channel, rq, on);
1953930dfa56SMin Li 		break;
19543a6ba7dcSVincent Cheng 	default:
19553a6ba7dcSVincent Cheng 		break;
19563a6ba7dcSVincent Cheng 	}
19573a6ba7dcSVincent Cheng 
1958930dfa56SMin Li 	mutex_unlock(idtcm->lock);
1959930dfa56SMin Li 
1960930dfa56SMin Li 	if (err)
1961930dfa56SMin Li 		dev_err(channel->idtcm->dev,
1962930dfa56SMin Li 			"Failed in %s with err %d!", __func__, err);
1963930dfa56SMin Li 
1964930dfa56SMin Li 	return err;
19653a6ba7dcSVincent Cheng }
19663a6ba7dcSVincent Cheng 
idtcm_enable_tod(struct idtcm_channel * channel)19673a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel)
19683a6ba7dcSVincent Cheng {
19693a6ba7dcSVincent Cheng 	struct idtcm *idtcm = channel->idtcm;
19703a6ba7dcSVincent Cheng 	struct timespec64 ts = {0, 0};
1971794c3dffSMin Li 	u16 tod_cfg = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_CFG);
19723a6ba7dcSVincent Cheng 	u8 cfg;
19733a6ba7dcSVincent Cheng 	int err;
19743a6ba7dcSVincent Cheng 
19753a6ba7dcSVincent Cheng 	/*
19763a6ba7dcSVincent Cheng 	 * Start the TOD clock ticking.
19773a6ba7dcSVincent Cheng 	 */
1978794c3dffSMin Li 	err = idtcm_read(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg));
19793a6ba7dcSVincent Cheng 	if (err)
19803a6ba7dcSVincent Cheng 		return err;
19813a6ba7dcSVincent Cheng 
19823a6ba7dcSVincent Cheng 	cfg |= TOD_ENABLE;
19833a6ba7dcSVincent Cheng 
1984794c3dffSMin Li 	err = idtcm_write(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg));
19853a6ba7dcSVincent Cheng 	if (err)
19863a6ba7dcSVincent Cheng 		return err;
19873a6ba7dcSVincent Cheng 
1988794c3dffSMin Li 	if (idtcm->fw_ver < V487)
1989da948233SMin Li 		return _idtcm_settime_deprecated(channel, &ts);
1990da948233SMin Li 	else
1991da948233SMin Li 		return _idtcm_settime(channel, &ts,
1992da948233SMin Li 				      SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
19933a6ba7dcSVincent Cheng }
19943a6ba7dcSVincent Cheng 
idtcm_set_version_info(struct idtcm * idtcm)1995da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm)
19963a6ba7dcSVincent Cheng {
19973a6ba7dcSVincent Cheng 	u8 major;
19983a6ba7dcSVincent Cheng 	u8 minor;
19993a6ba7dcSVincent Cheng 	u8 hotfix;
20003a6ba7dcSVincent Cheng 	u16 product_id;
20013a6ba7dcSVincent Cheng 	u8 hw_rev_id;
20021ece2fbeSVincent Cheng 	u8 config_select;
20033a6ba7dcSVincent Cheng 
20043a6ba7dcSVincent Cheng 	idtcm_read_major_release(idtcm, &major);
20053a6ba7dcSVincent Cheng 	idtcm_read_minor_release(idtcm, &minor);
20063a6ba7dcSVincent Cheng 	idtcm_read_hotfix_release(idtcm, &hotfix);
20073a6ba7dcSVincent Cheng 
20083a6ba7dcSVincent Cheng 	idtcm_read_product_id(idtcm, &product_id);
20093a6ba7dcSVincent Cheng 	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
20103a6ba7dcSVincent Cheng 
20111ece2fbeSVincent Cheng 	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
20121ece2fbeSVincent Cheng 
20137ea5fda2SMin Li 	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
20147ea5fda2SMin Li 		 major, minor, hotfix);
20157ea5fda2SMin Li 
2016794c3dffSMin Li 	idtcm->fw_ver = idtcm_fw_version(idtcm->version);
2017da948233SMin Li 
2018930dfa56SMin Li 	dev_info(idtcm->dev,
20191c49d3e9SVincent Cheng 		 "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d",
20201c49d3e9SVincent Cheng 		 major, minor, hotfix,
20211ece2fbeSVincent Cheng 		 product_id, hw_rev_id, config_select);
20223a6ba7dcSVincent Cheng }
20233a6ba7dcSVincent Cheng 
idtcm_verify_pin(struct ptp_clock_info * ptp,unsigned int pin,enum ptp_pin_function func,unsigned int chan)2024bec67592SMin Li static int idtcm_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
2025bec67592SMin Li 			    enum ptp_pin_function func, unsigned int chan)
2026bec67592SMin Li {
2027bec67592SMin Li 	switch (func) {
2028bec67592SMin Li 	case PTP_PF_NONE:
2029bec67592SMin Li 	case PTP_PF_EXTTS:
2030bec67592SMin Li 		break;
2031bec67592SMin Li 	case PTP_PF_PEROUT:
2032bec67592SMin Li 	case PTP_PF_PHYSYNC:
2033bec67592SMin Li 		return -1;
2034bec67592SMin Li 	}
2035bec67592SMin Li 	return 0;
2036bec67592SMin Li }
2037bec67592SMin Li 
2038bec67592SMin Li static struct ptp_pin_desc pin_config[MAX_TOD][MAX_REF_CLK];
2039bec67592SMin Li 
20406485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = {
20413a6ba7dcSVincent Cheng 	.owner		= THIS_MODULE,
20423a6ba7dcSVincent Cheng 	.max_adj	= 244000,
20437ea5fda2SMin Li 	.n_per_out	= 12,
2044930dfa56SMin Li 	.n_ext_ts	= MAX_TOD,
2045bec67592SMin Li 	.n_pins		= MAX_REF_CLK,
2046425d2b1cSVincent Cheng 	.adjphase	= &idtcm_adjphase,
2047*c066e74fSRahul Rameshbabu 	.getmaxphase	= &idtcm_getmaxphase,
20487ea5fda2SMin Li 	.adjfine	= &idtcm_adjfine,
20493a6ba7dcSVincent Cheng 	.adjtime	= &idtcm_adjtime,
20503a6ba7dcSVincent Cheng 	.gettime64	= &idtcm_gettime,
20513a6ba7dcSVincent Cheng 	.settime64	= &idtcm_settime,
20523a6ba7dcSVincent Cheng 	.enable		= &idtcm_enable,
2053bec67592SMin Li 	.verify		= &idtcm_verify_pin,
2054da9facf1SMin Li 	.do_aux_work	= &idtcm_work_handler,
20553a6ba7dcSVincent Cheng };
20563a6ba7dcSVincent Cheng 
2057da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = {
2058da948233SMin Li 	.owner		= THIS_MODULE,
2059da948233SMin Li 	.max_adj	= 244000,
2060da948233SMin Li 	.n_per_out	= 12,
2061930dfa56SMin Li 	.n_ext_ts	= MAX_TOD,
2062bec67592SMin Li 	.n_pins		= MAX_REF_CLK,
2063da948233SMin Li 	.adjphase	= &idtcm_adjphase,
2064*c066e74fSRahul Rameshbabu 	.getmaxphase    = &idtcm_getmaxphase,
2065da948233SMin Li 	.adjfine	= &idtcm_adjfine,
2066da948233SMin Li 	.adjtime	= &idtcm_adjtime_deprecated,
2067da948233SMin Li 	.gettime64	= &idtcm_gettime,
2068da948233SMin Li 	.settime64	= &idtcm_settime_deprecated,
2069da948233SMin Li 	.enable		= &idtcm_enable,
2070bec67592SMin Li 	.verify		= &idtcm_verify_pin,
2071da9facf1SMin Li 	.do_aux_work	= &idtcm_work_handler,
2072da948233SMin Li };
2073da948233SMin Li 
configure_channel_pll(struct idtcm_channel * channel)20747ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel)
20753a6ba7dcSVincent Cheng {
2076794c3dffSMin Li 	struct idtcm *idtcm = channel->idtcm;
20777ea5fda2SMin Li 	int err = 0;
20783a6ba7dcSVincent Cheng 
20797ea5fda2SMin Li 	switch (channel->pll) {
20803a6ba7dcSVincent Cheng 	case 0:
20813a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_0;
20823a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_0;
20833a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_0;
20843a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_0;
20853a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_0;
20863a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
20873a6ba7dcSVincent Cheng 		break;
20883a6ba7dcSVincent Cheng 	case 1:
20893a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_1;
20903a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_1;
20913a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_1;
20923a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_1;
20933a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_1;
20943a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
20953a6ba7dcSVincent Cheng 		break;
20963a6ba7dcSVincent Cheng 	case 2:
20973a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_2;
2098794c3dffSMin Li 		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_2);
20993a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_2;
21003a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_2;
21013a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_2;
21023a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
21033a6ba7dcSVincent Cheng 		break;
21043a6ba7dcSVincent Cheng 	case 3:
21053a6ba7dcSVincent Cheng 		channel->dpll_freq = DPLL_FREQ_3;
21063a6ba7dcSVincent Cheng 		channel->dpll_n = DPLL_3;
21073a6ba7dcSVincent Cheng 		channel->hw_dpll_n = HW_DPLL_3;
21083a6ba7dcSVincent Cheng 		channel->dpll_phase = DPLL_PHASE_3;
21093a6ba7dcSVincent Cheng 		channel->dpll_ctrl_n = DPLL_CTRL_3;
21103a6ba7dcSVincent Cheng 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
21113a6ba7dcSVincent Cheng 		break;
21127ea5fda2SMin Li 	case 4:
21137ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_4;
2114794c3dffSMin Li 		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_4);
21157ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_4;
21167ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_4;
21177ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_4;
21187ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
21197ea5fda2SMin Li 		break;
21207ea5fda2SMin Li 	case 5:
21217ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_5;
21227ea5fda2SMin Li 		channel->dpll_n = DPLL_5;
21237ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_5;
21247ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_5;
21257ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_5;
21267ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
21277ea5fda2SMin Li 		break;
21287ea5fda2SMin Li 	case 6:
21297ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_6;
2130794c3dffSMin Li 		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_6);
21317ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_6;
21327ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_6;
21337ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_6;
21347ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
21357ea5fda2SMin Li 		break;
21367ea5fda2SMin Li 	case 7:
21377ea5fda2SMin Li 		channel->dpll_freq = DPLL_FREQ_7;
21387ea5fda2SMin Li 		channel->dpll_n = DPLL_7;
21397ea5fda2SMin Li 		channel->hw_dpll_n = HW_DPLL_7;
21407ea5fda2SMin Li 		channel->dpll_phase = DPLL_PHASE_7;
21417ea5fda2SMin Li 		channel->dpll_ctrl_n = DPLL_CTRL_7;
21427ea5fda2SMin Li 		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
21437ea5fda2SMin Li 		break;
21447ea5fda2SMin Li 	default:
21457ea5fda2SMin Li 		err = -EINVAL;
21467ea5fda2SMin Li 	}
21477ea5fda2SMin Li 
21487ea5fda2SMin Li 	return err;
21497ea5fda2SMin Li }
21507ea5fda2SMin Li 
2151930dfa56SMin Li /*
2152930dfa56SMin Li  * Compensate for the PTP DCO input-to-output delay.
2153930dfa56SMin Li  * This delay is 18 FOD cycles.
2154930dfa56SMin Li  */
idtcm_get_dco_delay(struct idtcm_channel * channel)2155930dfa56SMin Li static u32 idtcm_get_dco_delay(struct idtcm_channel *channel)
21567ea5fda2SMin Li {
2157930dfa56SMin Li 	struct idtcm *idtcm = channel->idtcm;
2158930dfa56SMin Li 	u8 mbuf[8] = {0};
2159930dfa56SMin Li 	u8 nbuf[2] = {0};
2160930dfa56SMin Li 	u32 fodFreq;
21617ea5fda2SMin Li 	int err;
2162930dfa56SMin Li 	u64 m;
2163930dfa56SMin Li 	u16 n;
21647ea5fda2SMin Li 
2165930dfa56SMin Li 	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
2166930dfa56SMin Li 			 DPLL_CTRL_DPLL_FOD_FREQ, mbuf, 6);
21677ea5fda2SMin Li 	if (err)
2168930dfa56SMin Li 		return 0;
2169930dfa56SMin Li 
2170930dfa56SMin Li 	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
2171930dfa56SMin Li 			 DPLL_CTRL_DPLL_FOD_FREQ + 6, nbuf, 2);
2172930dfa56SMin Li 	if (err)
2173930dfa56SMin Li 		return 0;
2174930dfa56SMin Li 
2175930dfa56SMin Li 	m = get_unaligned_le64(mbuf);
2176930dfa56SMin Li 	n = get_unaligned_le16(nbuf);
2177930dfa56SMin Li 
2178930dfa56SMin Li 	if (n == 0)
2179930dfa56SMin Li 		n = 1;
2180930dfa56SMin Li 
2181930dfa56SMin Li 	fodFreq = (u32)div_u64(m, n);
2182bec67592SMin Li 
2183930dfa56SMin Li 	if (fodFreq >= 500000000)
2184bec67592SMin Li 		return (u32)div_u64(18 * (u64)NSEC_PER_SEC, fodFreq);
2185930dfa56SMin Li 
2186930dfa56SMin Li 	return 0;
2187930dfa56SMin Li }
2188930dfa56SMin Li 
configure_channel_tod(struct idtcm_channel * channel,u32 index)2189930dfa56SMin Li static int configure_channel_tod(struct idtcm_channel *channel, u32 index)
2190930dfa56SMin Li {
2191930dfa56SMin Li 	enum fw_version fw_ver = channel->idtcm->fw_ver;
21927ea5fda2SMin Li 
21937ea5fda2SMin Li 	/* Set tod addresses */
21947ea5fda2SMin Li 	switch (index) {
21957ea5fda2SMin Li 	case 0:
2196794c3dffSMin Li 		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0);
2197bec67592SMin Li 		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_0);
2198794c3dffSMin Li 		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0);
2199794c3dffSMin Li 		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
2200794c3dffSMin Li 		channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
22017ea5fda2SMin Li 		break;
22027ea5fda2SMin Li 	case 1:
2203794c3dffSMin Li 		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1);
2204bec67592SMin Li 		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_1);
2205794c3dffSMin Li 		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1);
2206794c3dffSMin Li 		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
2207794c3dffSMin Li 		channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
22087ea5fda2SMin Li 		break;
22097ea5fda2SMin Li 	case 2:
2210794c3dffSMin Li 		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2);
2211bec67592SMin Li 		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_2);
2212794c3dffSMin Li 		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2);
2213794c3dffSMin Li 		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
2214794c3dffSMin Li 		channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
22157ea5fda2SMin Li 		break;
22167ea5fda2SMin Li 	case 3:
2217794c3dffSMin Li 		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3);
2218bec67592SMin Li 		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_3);
2219794c3dffSMin Li 		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3);
2220794c3dffSMin Li 		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
2221794c3dffSMin Li 		channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
22227ea5fda2SMin Li 		break;
22233a6ba7dcSVincent Cheng 	default:
22243a6ba7dcSVincent Cheng 		return -EINVAL;
22253a6ba7dcSVincent Cheng 	}
22263a6ba7dcSVincent Cheng 
2227930dfa56SMin Li 	return 0;
2228930dfa56SMin Li }
2229930dfa56SMin Li 
idtcm_enable_channel(struct idtcm * idtcm,u32 index)2230930dfa56SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
2231930dfa56SMin Li {
2232930dfa56SMin Li 	struct idtcm_channel *channel;
2233930dfa56SMin Li 	int err;
2234bec67592SMin Li 	int i;
2235930dfa56SMin Li 
2236930dfa56SMin Li 	if (!(index < MAX_TOD))
2237930dfa56SMin Li 		return -EINVAL;
2238930dfa56SMin Li 
2239930dfa56SMin Li 	channel = &idtcm->channel[index];
2240930dfa56SMin Li 
2241930dfa56SMin Li 	channel->idtcm = idtcm;
2242930dfa56SMin Li 	channel->current_freq_scaled_ppm = 0;
2243930dfa56SMin Li 
2244930dfa56SMin Li 	/* Set pll addresses */
2245930dfa56SMin Li 	err = configure_channel_pll(channel);
2246930dfa56SMin Li 	if (err)
2247930dfa56SMin Li 		return err;
2248930dfa56SMin Li 
2249930dfa56SMin Li 	/* Set tod addresses */
2250930dfa56SMin Li 	err = configure_channel_tod(channel, index);
2251930dfa56SMin Li 	if (err)
2252930dfa56SMin Li 		return err;
2253930dfa56SMin Li 
2254794c3dffSMin Li 	if (idtcm->fw_ver < V487)
2255da948233SMin Li 		channel->caps = idtcm_caps_deprecated;
22567ea5fda2SMin Li 	else
22573a6ba7dcSVincent Cheng 		channel->caps = idtcm_caps;
22587ea5fda2SMin Li 
22593a6ba7dcSVincent Cheng 	snprintf(channel->caps.name, sizeof(channel->caps.name),
22607ea5fda2SMin Li 		 "IDT CM TOD%u", index);
22617ea5fda2SMin Li 
2262bec67592SMin Li 	channel->caps.pin_config = pin_config[index];
2263bec67592SMin Li 
2264bec67592SMin Li 	for (i = 0; i < channel->caps.n_pins; ++i) {
2265bec67592SMin Li 		struct ptp_pin_desc *ppd = &channel->caps.pin_config[i];
2266bec67592SMin Li 
2267bec67592SMin Li 		snprintf(ppd->name, sizeof(ppd->name), "input_ref%d", i);
2268bec67592SMin Li 		ppd->index = i;
2269bec67592SMin Li 		ppd->func = PTP_PF_NONE;
2270bec67592SMin Li 		ppd->chan = index;
2271bec67592SMin Li 	}
2272bec67592SMin Li 
2273da9facf1SMin Li 	err = initialize_dco_operating_mode(channel);
2274da9facf1SMin Li 	if (err)
22753a6ba7dcSVincent Cheng 		return err;
22763a6ba7dcSVincent Cheng 
22773a6ba7dcSVincent Cheng 	err = idtcm_enable_tod(channel);
22787ea5fda2SMin Li 	if (err) {
2279930dfa56SMin Li 		dev_err(idtcm->dev,
22801c49d3e9SVincent Cheng 			"Failed at line %d in %s!", __LINE__, __func__);
22813a6ba7dcSVincent Cheng 		return err;
22827ea5fda2SMin Li 	}
22833a6ba7dcSVincent Cheng 
2284930dfa56SMin Li 	channel->dco_delay = idtcm_get_dco_delay(channel);
2285930dfa56SMin Li 
22863a6ba7dcSVincent Cheng 	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
22873a6ba7dcSVincent Cheng 
22883a6ba7dcSVincent Cheng 	if (IS_ERR(channel->ptp_clock)) {
22893a6ba7dcSVincent Cheng 		err = PTR_ERR(channel->ptp_clock);
22903a6ba7dcSVincent Cheng 		channel->ptp_clock = NULL;
22913a6ba7dcSVincent Cheng 		return err;
22923a6ba7dcSVincent Cheng 	}
22933a6ba7dcSVincent Cheng 
22943a6ba7dcSVincent Cheng 	if (!channel->ptp_clock)
22953a6ba7dcSVincent Cheng 		return -ENOTSUPP;
22963a6ba7dcSVincent Cheng 
2297930dfa56SMin Li 	dev_info(idtcm->dev, "PLL%d registered as ptp%d",
22983a6ba7dcSVincent Cheng 		 index, channel->ptp_clock->index);
22993a6ba7dcSVincent Cheng 
23003a6ba7dcSVincent Cheng 	return 0;
23013a6ba7dcSVincent Cheng }
23023a6ba7dcSVincent Cheng 
idtcm_enable_extts_channel(struct idtcm * idtcm,u32 index)2303930dfa56SMin Li static int idtcm_enable_extts_channel(struct idtcm *idtcm, u32 index)
2304930dfa56SMin Li {
2305930dfa56SMin Li 	struct idtcm_channel *channel;
2306930dfa56SMin Li 	int err;
2307930dfa56SMin Li 
2308930dfa56SMin Li 	if (!(index < MAX_TOD))
2309930dfa56SMin Li 		return -EINVAL;
2310930dfa56SMin Li 
2311930dfa56SMin Li 	channel = &idtcm->channel[index];
2312930dfa56SMin Li 	channel->idtcm = idtcm;
2313930dfa56SMin Li 
2314930dfa56SMin Li 	/* Set tod addresses */
2315930dfa56SMin Li 	err = configure_channel_tod(channel, index);
2316930dfa56SMin Li 	if (err)
2317930dfa56SMin Li 		return err;
2318930dfa56SMin Li 
2319930dfa56SMin Li 	channel->idtcm = idtcm;
2320930dfa56SMin Li 
2321930dfa56SMin Li 	return 0;
2322930dfa56SMin Li }
2323930dfa56SMin Li 
idtcm_extts_check(struct work_struct * work)2324930dfa56SMin Li static void idtcm_extts_check(struct work_struct *work)
2325930dfa56SMin Li {
2326930dfa56SMin Li 	struct idtcm *idtcm = container_of(work, struct idtcm, extts_work.work);
2327bec67592SMin Li 	struct idtcm_channel *channel;
2328bec67592SMin Li 	u8 mask;
2329bec67592SMin Li 	int err;
2330bec67592SMin Li 	int i;
2331930dfa56SMin Li 
2332930dfa56SMin Li 	if (idtcm->extts_mask == 0)
2333930dfa56SMin Li 		return;
2334930dfa56SMin Li 
2335930dfa56SMin Li 	mutex_lock(idtcm->lock);
2336930dfa56SMin Li 
2337bec67592SMin Li 	for (i = 0; i < MAX_TOD; i++) {
2338bec67592SMin Li 		mask = 1 << i;
2339bec67592SMin Li 
2340bec67592SMin Li 		if ((idtcm->extts_mask & mask) == 0)
2341bec67592SMin Li 			continue;
2342bec67592SMin Li 
2343930dfa56SMin Li 		err = idtcm_extts_check_channel(idtcm, i);
2344bec67592SMin Li 
2345bec67592SMin Li 		if (err == 0) {
2346930dfa56SMin Li 			/* trigger clears itself, so clear the mask */
2347bec67592SMin Li 			if (idtcm->extts_single_shot) {
2348930dfa56SMin Li 				idtcm->extts_mask &= ~mask;
2349bec67592SMin Li 			} else {
2350bec67592SMin Li 				/* Re-arm */
2351bec67592SMin Li 				channel = &idtcm->channel[i];
2352bec67592SMin Li 				arm_tod_read_trig_sel_refclk(channel, channel->refn);
2353bec67592SMin Li 			}
2354930dfa56SMin Li 		}
2355930dfa56SMin Li 	}
2356930dfa56SMin Li 
2357930dfa56SMin Li 	if (idtcm->extts_mask)
2358930dfa56SMin Li 		schedule_delayed_work(&idtcm->extts_work,
2359930dfa56SMin Li 				      msecs_to_jiffies(EXTTS_PERIOD_MS));
2360bec67592SMin Li 
2361930dfa56SMin Li 	mutex_unlock(idtcm->lock);
2362930dfa56SMin Li }
2363930dfa56SMin Li 
ptp_clock_unregister_all(struct idtcm * idtcm)23643a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm)
23653a6ba7dcSVincent Cheng {
23663a6ba7dcSVincent Cheng 	u8 i;
23673a6ba7dcSVincent Cheng 	struct idtcm_channel *channel;
23683a6ba7dcSVincent Cheng 
23697ea5fda2SMin Li 	for (i = 0; i < MAX_TOD; i++) {
23703a6ba7dcSVincent Cheng 		channel = &idtcm->channel[i];
23713a6ba7dcSVincent Cheng 		if (channel->ptp_clock)
23723a6ba7dcSVincent Cheng 			ptp_clock_unregister(channel->ptp_clock);
23733a6ba7dcSVincent Cheng 	}
23743a6ba7dcSVincent Cheng }
23753a6ba7dcSVincent Cheng 
set_default_masks(struct idtcm * idtcm)23763a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm)
23773a6ba7dcSVincent Cheng {
23787ea5fda2SMin Li 	idtcm->tod_mask = DEFAULT_TOD_MASK;
2379930dfa56SMin Li 	idtcm->extts_mask = 0;
23807ea5fda2SMin Li 
2381bec67592SMin Li 	idtcm->channel[0].tod = 0;
2382bec67592SMin Li 	idtcm->channel[1].tod = 1;
2383bec67592SMin Li 	idtcm->channel[2].tod = 2;
2384bec67592SMin Li 	idtcm->channel[3].tod = 3;
2385bec67592SMin Li 
23867ea5fda2SMin Li 	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
23877ea5fda2SMin Li 	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
23887ea5fda2SMin Li 	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
23897ea5fda2SMin Li 	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
23903a6ba7dcSVincent Cheng 
23913a6ba7dcSVincent Cheng 	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
23923a6ba7dcSVincent Cheng 	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
23933a6ba7dcSVincent Cheng 	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
23943a6ba7dcSVincent Cheng 	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
23953a6ba7dcSVincent Cheng }
23963a6ba7dcSVincent Cheng 
idtcm_probe(struct platform_device * pdev)2397930dfa56SMin Li static int idtcm_probe(struct platform_device *pdev)
23983a6ba7dcSVincent Cheng {
2399930dfa56SMin Li 	struct rsmu_ddata *ddata = dev_get_drvdata(pdev->dev.parent);
24003a6ba7dcSVincent Cheng 	struct idtcm *idtcm;
24013a6ba7dcSVincent Cheng 	int err;
24023a6ba7dcSVincent Cheng 	u8 i;
24033a6ba7dcSVincent Cheng 
2404930dfa56SMin Li 	idtcm = devm_kzalloc(&pdev->dev, sizeof(struct idtcm), GFP_KERNEL);
24053a6ba7dcSVincent Cheng 
24063a6ba7dcSVincent Cheng 	if (!idtcm)
24073a6ba7dcSVincent Cheng 		return -ENOMEM;
24083a6ba7dcSVincent Cheng 
2409930dfa56SMin Li 	idtcm->dev = &pdev->dev;
2410930dfa56SMin Li 	idtcm->mfd = pdev->dev.parent;
2411930dfa56SMin Li 	idtcm->lock = &ddata->lock;
2412930dfa56SMin Li 	idtcm->regmap = ddata->regmap;
24133a6ba7dcSVincent Cheng 	idtcm->calculate_overhead_flag = 0;
24143a6ba7dcSVincent Cheng 
2415930dfa56SMin Li 	INIT_DELAYED_WORK(&idtcm->extts_work, idtcm_extts_check);
2416930dfa56SMin Li 
24173a6ba7dcSVincent Cheng 	set_default_masks(idtcm);
24183a6ba7dcSVincent Cheng 
2419930dfa56SMin Li 	mutex_lock(idtcm->lock);
24203a6ba7dcSVincent Cheng 
2421da948233SMin Li 	idtcm_set_version_info(idtcm);
24223a6ba7dcSVincent Cheng 
2423930dfa56SMin Li 	err = idtcm_load_firmware(idtcm, &pdev->dev);
2424930dfa56SMin Li 
24253a6ba7dcSVincent Cheng 	if (err)
2426930dfa56SMin Li 		dev_warn(idtcm->dev, "loading firmware failed with %d", err);
24273a6ba7dcSVincent Cheng 
2428797d3186SVincent Cheng 	wait_for_chip_ready(idtcm);
2429251f4fe2SMin Li 
24307ea5fda2SMin Li 	if (idtcm->tod_mask) {
24317ea5fda2SMin Li 		for (i = 0; i < MAX_TOD; i++) {
2432930dfa56SMin Li 			if (idtcm->tod_mask & (1 << i))
24333a6ba7dcSVincent Cheng 				err = idtcm_enable_channel(idtcm, i);
2434930dfa56SMin Li 			else
2435930dfa56SMin Li 				err = idtcm_enable_extts_channel(idtcm, i);
24367ea5fda2SMin Li 			if (err) {
2437930dfa56SMin Li 				dev_err(idtcm->dev,
24381c49d3e9SVincent Cheng 					"idtcm_enable_channel %d failed!", i);
24393a6ba7dcSVincent Cheng 				break;
24403a6ba7dcSVincent Cheng 			}
24413a6ba7dcSVincent Cheng 		}
24423a6ba7dcSVincent Cheng 	} else {
2443930dfa56SMin Li 		dev_err(idtcm->dev,
24441c49d3e9SVincent Cheng 			"no PLLs flagged as PHCs, nothing to do");
24453a6ba7dcSVincent Cheng 		err = -ENODEV;
24463a6ba7dcSVincent Cheng 	}
24473a6ba7dcSVincent Cheng 
2448930dfa56SMin Li 	mutex_unlock(idtcm->lock);
24493a6ba7dcSVincent Cheng 
24503a6ba7dcSVincent Cheng 	if (err) {
24513a6ba7dcSVincent Cheng 		ptp_clock_unregister_all(idtcm);
24523a6ba7dcSVincent Cheng 		return err;
24533a6ba7dcSVincent Cheng 	}
24543a6ba7dcSVincent Cheng 
2455930dfa56SMin Li 	platform_set_drvdata(pdev, idtcm);
24563a6ba7dcSVincent Cheng 
24573a6ba7dcSVincent Cheng 	return 0;
24583a6ba7dcSVincent Cheng }
24593a6ba7dcSVincent Cheng 
idtcm_remove(struct platform_device * pdev)2460930dfa56SMin Li static int idtcm_remove(struct platform_device *pdev)
24613a6ba7dcSVincent Cheng {
2462930dfa56SMin Li 	struct idtcm *idtcm = platform_get_drvdata(pdev);
24633a6ba7dcSVincent Cheng 
2464bec67592SMin Li 	idtcm->extts_mask = 0;
24653a6ba7dcSVincent Cheng 	ptp_clock_unregister_all(idtcm);
2466930dfa56SMin Li 	cancel_delayed_work_sync(&idtcm->extts_work);
24673a6ba7dcSVincent Cheng 
24683a6ba7dcSVincent Cheng 	return 0;
24693a6ba7dcSVincent Cheng }
24703a6ba7dcSVincent Cheng 
2471930dfa56SMin Li static struct platform_driver idtcm_driver = {
24723a6ba7dcSVincent Cheng 	.driver = {
2473930dfa56SMin Li 		.name = "8a3400x-phc",
24743a6ba7dcSVincent Cheng 	},
24753a6ba7dcSVincent Cheng 	.probe = idtcm_probe,
24763a6ba7dcSVincent Cheng 	.remove	= idtcm_remove,
24773a6ba7dcSVincent Cheng };
24783a6ba7dcSVincent Cheng 
2479930dfa56SMin Li module_platform_driver(idtcm_driver);
2480