18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PTP hardware clock driver for the IDT ClockMatrix(TM) family of timing and
48c2ecf20Sopenharmony_ci * synchronization devices.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/firmware.h>
98c2ecf20Sopenharmony_ci#include <linux/i2c.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/timekeeping.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "ptp_private.h"
198c2ecf20Sopenharmony_ci#include "ptp_clockmatrix.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family");
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
238c2ecf20Sopenharmony_ciMODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
248c2ecf20Sopenharmony_ciMODULE_VERSION("1.0");
258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * The name of the firmware file to be loaded
298c2ecf20Sopenharmony_ci * over-rides any automatic selection
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic char *firmware;
328c2ecf20Sopenharmony_cimodule_param(firmware, charp, 0);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SETTIME_CORRECTION (0)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic long set_write_phase_ready(struct ptp_clock_info *ptp)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
398c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	channel->write_phase_ready = 1;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int char_array_to_timespec(u8 *buf,
478c2ecf20Sopenharmony_ci				  u8 count,
488c2ecf20Sopenharmony_ci				  struct timespec64 *ts)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	u8 i;
518c2ecf20Sopenharmony_ci	u64 nsec;
528c2ecf20Sopenharmony_ci	time64_t sec;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (count < TOD_BYTE_COUNT)
558c2ecf20Sopenharmony_ci		return 1;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Sub-nanoseconds are in buf[0]. */
588c2ecf20Sopenharmony_ci	nsec = buf[4];
598c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
608c2ecf20Sopenharmony_ci		nsec <<= 8;
618c2ecf20Sopenharmony_ci		nsec |= buf[3 - i];
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	sec = buf[10];
658c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
668c2ecf20Sopenharmony_ci		sec <<= 8;
678c2ecf20Sopenharmony_ci		sec |= buf[9 - i];
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ts->tv_sec = sec;
718c2ecf20Sopenharmony_ci	ts->tv_nsec = nsec;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int timespec_to_char_array(struct timespec64 const *ts,
778c2ecf20Sopenharmony_ci				  u8 *buf,
788c2ecf20Sopenharmony_ci				  u8 count)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	u8 i;
818c2ecf20Sopenharmony_ci	s32 nsec;
828c2ecf20Sopenharmony_ci	time64_t sec;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (count < TOD_BYTE_COUNT)
858c2ecf20Sopenharmony_ci		return 1;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	nsec = ts->tv_nsec;
888c2ecf20Sopenharmony_ci	sec = ts->tv_sec;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Sub-nanoseconds are in buf[0]. */
918c2ecf20Sopenharmony_ci	buf[0] = 0;
928c2ecf20Sopenharmony_ci	for (i = 1; i < 5; i++) {
938c2ecf20Sopenharmony_ci		buf[i] = nsec & 0xff;
948c2ecf20Sopenharmony_ci		nsec >>= 8;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	for (i = 5; i < TOD_BYTE_COUNT; i++) {
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		buf[i] = sec & 0xff;
1008c2ecf20Sopenharmony_ci		sec >>= 8;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int idtcm_strverscmp(const char *version1, const char *version2)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	u8 ver1[3], ver2[3];
1098c2ecf20Sopenharmony_ci	int i;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (sscanf(version1, "%hhu.%hhu.%hhu",
1128c2ecf20Sopenharmony_ci		   &ver1[0], &ver1[1], &ver1[2]) != 3)
1138c2ecf20Sopenharmony_ci		return -1;
1148c2ecf20Sopenharmony_ci	if (sscanf(version2, "%hhu.%hhu.%hhu",
1158c2ecf20Sopenharmony_ci		   &ver2[0], &ver2[1], &ver2[2]) != 3)
1168c2ecf20Sopenharmony_ci		return -1;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
1198c2ecf20Sopenharmony_ci		if (ver1[i] > ver2[i])
1208c2ecf20Sopenharmony_ci			return 1;
1218c2ecf20Sopenharmony_ci		if (ver1[i] < ver2[i])
1228c2ecf20Sopenharmony_ci			return -1;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int idtcm_xfer_read(struct idtcm *idtcm,
1298c2ecf20Sopenharmony_ci			   u8 regaddr,
1308c2ecf20Sopenharmony_ci			   u8 *buf,
1318c2ecf20Sopenharmony_ci			   u16 count)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct i2c_client *client = idtcm->client;
1348c2ecf20Sopenharmony_ci	struct i2c_msg msg[2];
1358c2ecf20Sopenharmony_ci	int cnt;
1368c2ecf20Sopenharmony_ci	char *fmt = "i2c_transfer failed at %d in %s, at addr: %04X!\n";
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	msg[0].addr = client->addr;
1398c2ecf20Sopenharmony_ci	msg[0].flags = 0;
1408c2ecf20Sopenharmony_ci	msg[0].len = 1;
1418c2ecf20Sopenharmony_ci	msg[0].buf = &regaddr;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	msg[1].addr = client->addr;
1448c2ecf20Sopenharmony_ci	msg[1].flags = I2C_M_RD;
1458c2ecf20Sopenharmony_ci	msg[1].len = count;
1468c2ecf20Sopenharmony_ci	msg[1].buf = buf;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	cnt = i2c_transfer(client->adapter, msg, 2);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (cnt < 0) {
1518c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1528c2ecf20Sopenharmony_ci			fmt,
1538c2ecf20Sopenharmony_ci			__LINE__,
1548c2ecf20Sopenharmony_ci			__func__,
1558c2ecf20Sopenharmony_ci			regaddr);
1568c2ecf20Sopenharmony_ci		return cnt;
1578c2ecf20Sopenharmony_ci	} else if (cnt != 2) {
1588c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1598c2ecf20Sopenharmony_ci			"i2c_transfer sent only %d of %d messages\n", cnt, 2);
1608c2ecf20Sopenharmony_ci		return -EIO;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int idtcm_xfer_write(struct idtcm *idtcm,
1678c2ecf20Sopenharmony_ci			    u8 regaddr,
1688c2ecf20Sopenharmony_ci			    u8 *buf,
1698c2ecf20Sopenharmony_ci			    u16 count)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct i2c_client *client = idtcm->client;
1728c2ecf20Sopenharmony_ci	/* we add 1 byte for device register */
1738c2ecf20Sopenharmony_ci	u8 msg[IDTCM_MAX_WRITE_COUNT + 1];
1748c2ecf20Sopenharmony_ci	int cnt;
1758c2ecf20Sopenharmony_ci	char *fmt = "i2c_master_send failed at %d in %s, at addr: %04X!\n";
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (count > IDTCM_MAX_WRITE_COUNT)
1788c2ecf20Sopenharmony_ci		return -EINVAL;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	msg[0] = regaddr;
1818c2ecf20Sopenharmony_ci	memcpy(&msg[1], buf, count);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	cnt = i2c_master_send(client, msg, count + 1);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (cnt < 0) {
1868c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1878c2ecf20Sopenharmony_ci			fmt,
1888c2ecf20Sopenharmony_ci			__LINE__,
1898c2ecf20Sopenharmony_ci			__func__,
1908c2ecf20Sopenharmony_ci			regaddr);
1918c2ecf20Sopenharmony_ci		return cnt;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int idtcm_page_offset(struct idtcm *idtcm, u8 val)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	u8 buf[4];
2008c2ecf20Sopenharmony_ci	int err;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (idtcm->page_offset == val)
2038c2ecf20Sopenharmony_ci		return 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	buf[0] = 0x0;
2068c2ecf20Sopenharmony_ci	buf[1] = val;
2078c2ecf20Sopenharmony_ci	buf[2] = 0x10;
2088c2ecf20Sopenharmony_ci	buf[3] = 0x20;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf));
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (err) {
2138c2ecf20Sopenharmony_ci		idtcm->page_offset = 0xff;
2148c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev, "failed to set page offset\n");
2158c2ecf20Sopenharmony_ci	} else {
2168c2ecf20Sopenharmony_ci		idtcm->page_offset = val;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return err;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int _idtcm_rdwr(struct idtcm *idtcm,
2238c2ecf20Sopenharmony_ci		       u16 regaddr,
2248c2ecf20Sopenharmony_ci		       u8 *buf,
2258c2ecf20Sopenharmony_ci		       u16 count,
2268c2ecf20Sopenharmony_ci		       bool write)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u8 hi;
2298c2ecf20Sopenharmony_ci	u8 lo;
2308c2ecf20Sopenharmony_ci	int err;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	hi = (regaddr >> 8) & 0xff;
2338c2ecf20Sopenharmony_ci	lo = regaddr & 0xff;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	err = idtcm_page_offset(idtcm, hi);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (err)
2388c2ecf20Sopenharmony_ci		return err;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (write)
2418c2ecf20Sopenharmony_ci		return idtcm_xfer_write(idtcm, lo, buf, count);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return idtcm_xfer_read(idtcm, lo, buf, count);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int idtcm_read(struct idtcm *idtcm,
2478c2ecf20Sopenharmony_ci		      u16 module,
2488c2ecf20Sopenharmony_ci		      u16 regaddr,
2498c2ecf20Sopenharmony_ci		      u8 *buf,
2508c2ecf20Sopenharmony_ci		      u16 count)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int idtcm_write(struct idtcm *idtcm,
2568c2ecf20Sopenharmony_ci		       u16 module,
2578c2ecf20Sopenharmony_ci		       u16 regaddr,
2588c2ecf20Sopenharmony_ci		       u8 *buf,
2598c2ecf20Sopenharmony_ci		       u16 count)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int _idtcm_gettime(struct idtcm_channel *channel,
2658c2ecf20Sopenharmony_ci			  struct timespec64 *ts)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
2688c2ecf20Sopenharmony_ci	u8 buf[TOD_BYTE_COUNT];
2698c2ecf20Sopenharmony_ci	u8 timeout = 10;
2708c2ecf20Sopenharmony_ci	u8 trigger;
2718c2ecf20Sopenharmony_ci	int err;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_read_primary,
2748c2ecf20Sopenharmony_ci			 TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
2758c2ecf20Sopenharmony_ci	if (err)
2768c2ecf20Sopenharmony_ci		return err;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
2798c2ecf20Sopenharmony_ci	trigger |= (1 << TOD_READ_TRIGGER_SHIFT);
2808c2ecf20Sopenharmony_ci	trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_read_primary,
2838c2ecf20Sopenharmony_ci			  TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger));
2848c2ecf20Sopenharmony_ci	if (err)
2858c2ecf20Sopenharmony_ci		return err;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* wait trigger to be 0 */
2888c2ecf20Sopenharmony_ci	while (trigger & TOD_READ_TRIGGER_MASK) {
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		if (idtcm->calculate_overhead_flag)
2918c2ecf20Sopenharmony_ci			idtcm->start_time = ktime_get_raw();
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, channel->tod_read_primary,
2948c2ecf20Sopenharmony_ci				 TOD_READ_PRIMARY_CMD, &trigger,
2958c2ecf20Sopenharmony_ci				 sizeof(trigger));
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		if (err)
2988c2ecf20Sopenharmony_ci			return err;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		if (--timeout == 0)
3018c2ecf20Sopenharmony_ci			return -EIO;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_read_primary,
3058c2ecf20Sopenharmony_ci			 TOD_READ_PRIMARY, buf, sizeof(buf));
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (err)
3088c2ecf20Sopenharmony_ci		return err;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	err = char_array_to_timespec(buf, sizeof(buf), ts);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return err;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int _sync_pll_output(struct idtcm *idtcm,
3168c2ecf20Sopenharmony_ci			    u8 pll,
3178c2ecf20Sopenharmony_ci			    u8 sync_src,
3188c2ecf20Sopenharmony_ci			    u8 qn,
3198c2ecf20Sopenharmony_ci			    u8 qn_plus_1)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int err;
3228c2ecf20Sopenharmony_ci	u8 val;
3238c2ecf20Sopenharmony_ci	u16 sync_ctrl0;
3248c2ecf20Sopenharmony_ci	u16 sync_ctrl1;
3258c2ecf20Sopenharmony_ci	u8 temp;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if ((qn == 0) && (qn_plus_1 == 0))
3288c2ecf20Sopenharmony_ci		return 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (pll) {
3318c2ecf20Sopenharmony_ci	case 0:
3328c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
3338c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case 1:
3368c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
3378c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	case 2:
3408c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
3418c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	case 3:
3448c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
3458c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
3468c2ecf20Sopenharmony_ci		break;
3478c2ecf20Sopenharmony_ci	case 4:
3488c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
3498c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	case 5:
3528c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
3538c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case 6:
3568c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
3578c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci	case 7:
3608c2ecf20Sopenharmony_ci		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
3618c2ecf20Sopenharmony_ci		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
3628c2ecf20Sopenharmony_ci		break;
3638c2ecf20Sopenharmony_ci	default:
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	val = SYNCTRL1_MASTER_SYNC_RST;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Place master sync in reset */
3708c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
3718c2ecf20Sopenharmony_ci	if (err)
3728c2ecf20Sopenharmony_ci		return err;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
3758c2ecf20Sopenharmony_ci	if (err)
3768c2ecf20Sopenharmony_ci		return err;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Set sync trigger mask */
3798c2ecf20Sopenharmony_ci	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (qn)
3828c2ecf20Sopenharmony_ci		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (qn_plus_1)
3858c2ecf20Sopenharmony_ci		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
3888c2ecf20Sopenharmony_ci	if (err)
3898c2ecf20Sopenharmony_ci		return err;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* PLL5 can have OUT8 as second additional output. */
3928c2ecf20Sopenharmony_ci	if ((pll == 5) && (qn_plus_1 != 0)) {
3938c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
3948c2ecf20Sopenharmony_ci				 &temp, sizeof(temp));
3958c2ecf20Sopenharmony_ci		if (err)
3968c2ecf20Sopenharmony_ci			return err;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
4018c2ecf20Sopenharmony_ci				  &temp, sizeof(temp));
4028c2ecf20Sopenharmony_ci		if (err)
4038c2ecf20Sopenharmony_ci			return err;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		temp |= Q9_TO_Q8_SYNC_TRIG;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
4088c2ecf20Sopenharmony_ci				  &temp, sizeof(temp));
4098c2ecf20Sopenharmony_ci		if (err)
4108c2ecf20Sopenharmony_ci			return err;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/* PLL6 can have OUT11 as second additional output. */
4148c2ecf20Sopenharmony_ci	if ((pll == 6) && (qn_plus_1 != 0)) {
4158c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
4168c2ecf20Sopenharmony_ci				 &temp, sizeof(temp));
4178c2ecf20Sopenharmony_ci		if (err)
4188c2ecf20Sopenharmony_ci			return err;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
4238c2ecf20Sopenharmony_ci				  &temp, sizeof(temp));
4248c2ecf20Sopenharmony_ci		if (err)
4258c2ecf20Sopenharmony_ci			return err;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		temp |= Q10_TO_Q11_SYNC_TRIG;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
4308c2ecf20Sopenharmony_ci				  &temp, sizeof(temp));
4318c2ecf20Sopenharmony_ci		if (err)
4328c2ecf20Sopenharmony_ci			return err;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Place master sync out of reset */
4368c2ecf20Sopenharmony_ci	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
4378c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return err;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	int err = 0;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	switch (tod_addr) {
4478c2ecf20Sopenharmony_ci	case TOD_0:
4488c2ecf20Sopenharmony_ci		*sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci	case TOD_1:
4518c2ecf20Sopenharmony_ci		*sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
4528c2ecf20Sopenharmony_ci		break;
4538c2ecf20Sopenharmony_ci	case TOD_2:
4548c2ecf20Sopenharmony_ci		*sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
4558c2ecf20Sopenharmony_ci		break;
4568c2ecf20Sopenharmony_ci	case TOD_3:
4578c2ecf20Sopenharmony_ci		*sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	default:
4608c2ecf20Sopenharmony_ci		err = -EINVAL;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return err;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int idtcm_sync_pps_output(struct idtcm_channel *channel)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	u8 pll;
4718c2ecf20Sopenharmony_ci	u8 sync_src;
4728c2ecf20Sopenharmony_ci	u8 qn;
4738c2ecf20Sopenharmony_ci	u8 qn_plus_1;
4748c2ecf20Sopenharmony_ci	int err = 0;
4758c2ecf20Sopenharmony_ci	u8 out8_mux = 0;
4768c2ecf20Sopenharmony_ci	u8 out11_mux = 0;
4778c2ecf20Sopenharmony_ci	u8 temp;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	u16 output_mask = channel->output_mask;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src);
4828c2ecf20Sopenharmony_ci	if (err)
4838c2ecf20Sopenharmony_ci		return err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
4868c2ecf20Sopenharmony_ci			 &temp, sizeof(temp));
4878c2ecf20Sopenharmony_ci	if (err)
4888c2ecf20Sopenharmony_ci		return err;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
4918c2ecf20Sopenharmony_ci	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
4928c2ecf20Sopenharmony_ci		out8_mux = 1;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
4958c2ecf20Sopenharmony_ci			 &temp, sizeof(temp));
4968c2ecf20Sopenharmony_ci	if (err)
4978c2ecf20Sopenharmony_ci		return err;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
5008c2ecf20Sopenharmony_ci	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
5018c2ecf20Sopenharmony_ci		out11_mux = 1;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	for (pll = 0; pll < 8; pll++) {
5048c2ecf20Sopenharmony_ci		qn = 0;
5058c2ecf20Sopenharmony_ci		qn_plus_1 = 0;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		if (pll < 4) {
5088c2ecf20Sopenharmony_ci			/* First 4 pll has 2 outputs */
5098c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
5108c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
5118c2ecf20Sopenharmony_ci			qn_plus_1 = output_mask & 0x1;
5128c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
5138c2ecf20Sopenharmony_ci		} else if (pll == 4) {
5148c2ecf20Sopenharmony_ci			if (out8_mux == 0) {
5158c2ecf20Sopenharmony_ci				qn = output_mask & 0x1;
5168c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
5178c2ecf20Sopenharmony_ci			}
5188c2ecf20Sopenharmony_ci		} else if (pll == 5) {
5198c2ecf20Sopenharmony_ci			if (out8_mux) {
5208c2ecf20Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
5218c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
5228c2ecf20Sopenharmony_ci			}
5238c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
5248c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
5258c2ecf20Sopenharmony_ci		} else if (pll == 6) {
5268c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
5278c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
5288c2ecf20Sopenharmony_ci			if (out11_mux) {
5298c2ecf20Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
5308c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
5318c2ecf20Sopenharmony_ci			}
5328c2ecf20Sopenharmony_ci		} else if (pll == 7) {
5338c2ecf20Sopenharmony_ci			if (out11_mux == 0) {
5348c2ecf20Sopenharmony_ci				qn = output_mask & 0x1;
5358c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
5368c2ecf20Sopenharmony_ci			}
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		if ((qn != 0) || (qn_plus_1 != 0))
5408c2ecf20Sopenharmony_ci			err = _sync_pll_output(idtcm, pll, sync_src, qn,
5418c2ecf20Sopenharmony_ci					       qn_plus_1);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		if (err)
5448c2ecf20Sopenharmony_ci			return err;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return err;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
5518c2ecf20Sopenharmony_ci			       struct timespec64 const *ts,
5528c2ecf20Sopenharmony_ci			       enum hw_tod_write_trig_sel wr_trig)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	u8 buf[TOD_BYTE_COUNT];
5578c2ecf20Sopenharmony_ci	u8 cmd;
5588c2ecf20Sopenharmony_ci	int err;
5598c2ecf20Sopenharmony_ci	struct timespec64 local_ts = *ts;
5608c2ecf20Sopenharmony_ci	s64 total_overhead_ns;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* Configure HW TOD write trigger. */
5638c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
5648c2ecf20Sopenharmony_ci			 &cmd, sizeof(cmd));
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (err)
5678c2ecf20Sopenharmony_ci		return err;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	cmd &= ~(0x0f);
5708c2ecf20Sopenharmony_ci	cmd |= wr_trig | 0x08;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
5738c2ecf20Sopenharmony_ci			  &cmd, sizeof(cmd));
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (err)
5768c2ecf20Sopenharmony_ci		return err;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		if (err)
5838c2ecf20Sopenharmony_ci			return err;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
5868c2ecf20Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		if (err)
5898c2ecf20Sopenharmony_ci			return err;
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* ARM HW TOD write trigger. */
5938c2ecf20Sopenharmony_ci	cmd &= ~(0x08);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
5968c2ecf20Sopenharmony_ci			  &cmd, sizeof(cmd));
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		if (idtcm->calculate_overhead_flag) {
6018c2ecf20Sopenharmony_ci			/* Assumption: I2C @ 400KHz */
6028c2ecf20Sopenharmony_ci			total_overhead_ns =  ktime_to_ns(ktime_get_raw()
6038c2ecf20Sopenharmony_ci							 - idtcm->start_time)
6048c2ecf20Sopenharmony_ci					     + idtcm->tod_write_overhead_ns
6058c2ecf20Sopenharmony_ci					     + SETTIME_CORRECTION;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci			timespec64_add_ns(&local_ts, total_overhead_ns);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci			idtcm->calculate_overhead_flag = 0;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci		if (err)
6158c2ecf20Sopenharmony_ci			return err;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
6188c2ecf20Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	return err;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
6258c2ecf20Sopenharmony_ci				    struct timespec64 const *ts,
6268c2ecf20Sopenharmony_ci				    enum scsr_tod_write_trig_sel wr_trig,
6278c2ecf20Sopenharmony_ci				    enum scsr_tod_write_type_sel wr_type)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
6308c2ecf20Sopenharmony_ci	unsigned char buf[TOD_BYTE_COUNT], cmd;
6318c2ecf20Sopenharmony_ci	struct timespec64 local_ts = *ts;
6328c2ecf20Sopenharmony_ci	int err, count = 0;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (err)
6398c2ecf20Sopenharmony_ci		return err;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
6428c2ecf20Sopenharmony_ci			  buf, sizeof(buf));
6438c2ecf20Sopenharmony_ci	if (err)
6448c2ecf20Sopenharmony_ci		return err;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Trigger the write operation. */
6478c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
6488c2ecf20Sopenharmony_ci			 &cmd, sizeof(cmd));
6498c2ecf20Sopenharmony_ci	if (err)
6508c2ecf20Sopenharmony_ci		return err;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
6538c2ecf20Sopenharmony_ci	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
6548c2ecf20Sopenharmony_ci	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
6558c2ecf20Sopenharmony_ci	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
6588c2ecf20Sopenharmony_ci			   &cmd, sizeof(cmd));
6598c2ecf20Sopenharmony_ci	if (err)
6608c2ecf20Sopenharmony_ci		return err;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Wait for the operation to complete. */
6638c2ecf20Sopenharmony_ci	while (1) {
6648c2ecf20Sopenharmony_ci		/* pps trigger takes up to 1 sec to complete */
6658c2ecf20Sopenharmony_ci		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
6668c2ecf20Sopenharmony_ci			msleep(50);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
6698c2ecf20Sopenharmony_ci				 &cmd, sizeof(cmd));
6708c2ecf20Sopenharmony_ci		if (err)
6718c2ecf20Sopenharmony_ci			return err;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		if (cmd == 0)
6748c2ecf20Sopenharmony_ci			break;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		if (++count > 20) {
6778c2ecf20Sopenharmony_ci			dev_err(&idtcm->client->dev,
6788c2ecf20Sopenharmony_ci				"Timed out waiting for the write counter\n");
6798c2ecf20Sopenharmony_ci			return -EIO;
6808c2ecf20Sopenharmony_ci		}
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	return 0;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic int _idtcm_settime(struct idtcm_channel *channel,
6878c2ecf20Sopenharmony_ci			  struct timespec64 const *ts,
6888c2ecf20Sopenharmony_ci			  enum hw_tod_write_trig_sel wr_trig)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
6918c2ecf20Sopenharmony_ci	int err;
6928c2ecf20Sopenharmony_ci	int i;
6938c2ecf20Sopenharmony_ci	u8 trig_sel;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	err = _idtcm_set_dpll_hw_tod(channel, ts, wr_trig);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (err)
6988c2ecf20Sopenharmony_ci		return err;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* Wait for the operation to complete. */
7018c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
7028c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, channel->hw_dpll_n,
7038c2ecf20Sopenharmony_ci				 HW_DPLL_TOD_CTRL_1, &trig_sel,
7048c2ecf20Sopenharmony_ci				 sizeof(trig_sel));
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		if (err)
7078c2ecf20Sopenharmony_ci			return err;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci		if (trig_sel == 0x4a)
7108c2ecf20Sopenharmony_ci			break;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		err = 1;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (err) {
7168c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
7178c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
7188c2ecf20Sopenharmony_ci			__LINE__,
7198c2ecf20Sopenharmony_ci			__func__);
7208c2ecf20Sopenharmony_ci		return err;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	return idtcm_sync_pps_output(channel);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic int _idtcm_settime_v487(struct idtcm_channel *channel,
7278c2ecf20Sopenharmony_ci			       struct timespec64 const *ts,
7288c2ecf20Sopenharmony_ci			       enum scsr_tod_write_type_sel wr_type)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	return _idtcm_set_dpll_scsr_tod(channel, ts,
7318c2ecf20Sopenharmony_ci					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
7328c2ecf20Sopenharmony_ci					wr_type);
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
7368c2ecf20Sopenharmony_ci					  s32 offset_ns)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	int err;
7398c2ecf20Sopenharmony_ci	int i;
7408c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	u8 buf[4];
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
7458c2ecf20Sopenharmony_ci		buf[i] = 0xff & (offset_ns);
7468c2ecf20Sopenharmony_ci		offset_ns >>= 8;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
7508c2ecf20Sopenharmony_ci			  buf, sizeof(buf));
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return err;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
7568c2ecf20Sopenharmony_ci					       u32 max_ffo_ppb)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	int err;
7598c2ecf20Sopenharmony_ci	u8 i;
7608c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	u8 buf[3];
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (max_ffo_ppb & 0xff000000)
7658c2ecf20Sopenharmony_ci		max_ffo_ppb = 0;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
7688c2ecf20Sopenharmony_ci		buf[i] = 0xff & (max_ffo_ppb);
7698c2ecf20Sopenharmony_ci		max_ffo_ppb >>= 8;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
7738c2ecf20Sopenharmony_ci			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return err;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	int err;
7818c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	u8 buf;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
7868c2ecf20Sopenharmony_ci			 &buf, sizeof(buf));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (err)
7898c2ecf20Sopenharmony_ci		return err;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (buf == 0) {
7928c2ecf20Sopenharmony_ci		buf = 0x01;
7938c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
7948c2ecf20Sopenharmony_ci				  PULL_IN_CTRL, &buf, sizeof(buf));
7958c2ecf20Sopenharmony_ci	} else {
7968c2ecf20Sopenharmony_ci		err = -EBUSY;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return err;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int idtcm_do_phase_pull_in(struct idtcm_channel *channel,
8038c2ecf20Sopenharmony_ci				  s32 offset_ns,
8048c2ecf20Sopenharmony_ci				  u32 max_ffo_ppb)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	int err;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (err)
8118c2ecf20Sopenharmony_ci		return err;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (err)
8168c2ecf20Sopenharmony_ci		return err;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	err = idtcm_start_phase_pull_in(channel);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	return err;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int set_tod_write_overhead(struct idtcm_channel *channel)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
8268c2ecf20Sopenharmony_ci	s64 current_ns = 0;
8278c2ecf20Sopenharmony_ci	s64 lowest_ns = 0;
8288c2ecf20Sopenharmony_ci	int err;
8298c2ecf20Sopenharmony_ci	u8 i;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	ktime_t start;
8328c2ecf20Sopenharmony_ci	ktime_t stop;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	char buf[TOD_BYTE_COUNT] = {0};
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	/* Set page offset */
8378c2ecf20Sopenharmony_ci	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
8388c2ecf20Sopenharmony_ci		    buf, sizeof(buf));
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci		start = ktime_get_raw();
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
8458c2ecf20Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		if (err)
8488c2ecf20Sopenharmony_ci			return err;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		stop = ktime_get_raw();
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		current_ns = ktime_to_ns(stop - start);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		if (i == 0) {
8558c2ecf20Sopenharmony_ci			lowest_ns = current_ns;
8568c2ecf20Sopenharmony_ci		} else {
8578c2ecf20Sopenharmony_ci			if (current_ns < lowest_ns)
8588c2ecf20Sopenharmony_ci				lowest_ns = current_ns;
8598c2ecf20Sopenharmony_ci		}
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	idtcm->tod_write_overhead_ns = lowest_ns;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	return err;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	int err;
8708c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
8718c2ecf20Sopenharmony_ci	struct timespec64 ts;
8728c2ecf20Sopenharmony_ci	s64 now;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
8758c2ecf20Sopenharmony_ci		err = idtcm_do_phase_pull_in(channel, delta, 0);
8768c2ecf20Sopenharmony_ci	} else {
8778c2ecf20Sopenharmony_ci		idtcm->calculate_overhead_flag = 1;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		err = set_tod_write_overhead(channel);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci		if (err)
8828c2ecf20Sopenharmony_ci			return err;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci		err = _idtcm_gettime(channel, &ts);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		if (err)
8878c2ecf20Sopenharmony_ci			return err;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		now = timespec64_to_ns(&ts);
8908c2ecf20Sopenharmony_ci		now += delta;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		ts = ns_to_timespec64(now);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		err = _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	return err;
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic int idtcm_state_machine_reset(struct idtcm *idtcm)
9018c2ecf20Sopenharmony_ci{
9028c2ecf20Sopenharmony_ci	int err;
9038c2ecf20Sopenharmony_ci	u8 byte = SM_RESET_CMD;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	if (!err)
9088c2ecf20Sopenharmony_ci		msleep_interruptible(POST_SM_RESET_DELAY_MS);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	return err;
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_cistatic int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	int err;
9218c2ecf20Sopenharmony_ci	u8 buf[2] = {0};
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	*product_id = (buf[1] << 8) | buf[0];
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return err;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	int err;
9338c2ecf20Sopenharmony_ci	u8 buf = 0;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	*major = buf >> 1;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	return err;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	return idtcm_read(idtcm,
9508c2ecf20Sopenharmony_ci			  GENERAL_STATUS,
9518c2ecf20Sopenharmony_ci			  HOTFIX_REL,
9528c2ecf20Sopenharmony_ci			  hotfix,
9538c2ecf20Sopenharmony_ci			  sizeof(u8));
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_cistatic int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
9578c2ecf20Sopenharmony_ci					     u8 *config_select)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
9608c2ecf20Sopenharmony_ci			  config_select, sizeof(u8));
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	int err = 0;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	switch (addr) {
9688c2ecf20Sopenharmony_ci	case TOD0_OUT_ALIGN_MASK_ADDR:
9698c2ecf20Sopenharmony_ci		SET_U16_LSB(idtcm->channel[0].output_mask, val);
9708c2ecf20Sopenharmony_ci		break;
9718c2ecf20Sopenharmony_ci	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
9728c2ecf20Sopenharmony_ci		SET_U16_MSB(idtcm->channel[0].output_mask, val);
9738c2ecf20Sopenharmony_ci		break;
9748c2ecf20Sopenharmony_ci	case TOD1_OUT_ALIGN_MASK_ADDR:
9758c2ecf20Sopenharmony_ci		SET_U16_LSB(idtcm->channel[1].output_mask, val);
9768c2ecf20Sopenharmony_ci		break;
9778c2ecf20Sopenharmony_ci	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
9788c2ecf20Sopenharmony_ci		SET_U16_MSB(idtcm->channel[1].output_mask, val);
9798c2ecf20Sopenharmony_ci		break;
9808c2ecf20Sopenharmony_ci	case TOD2_OUT_ALIGN_MASK_ADDR:
9818c2ecf20Sopenharmony_ci		SET_U16_LSB(idtcm->channel[2].output_mask, val);
9828c2ecf20Sopenharmony_ci		break;
9838c2ecf20Sopenharmony_ci	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
9848c2ecf20Sopenharmony_ci		SET_U16_MSB(idtcm->channel[2].output_mask, val);
9858c2ecf20Sopenharmony_ci		break;
9868c2ecf20Sopenharmony_ci	case TOD3_OUT_ALIGN_MASK_ADDR:
9878c2ecf20Sopenharmony_ci		SET_U16_LSB(idtcm->channel[3].output_mask, val);
9888c2ecf20Sopenharmony_ci		break;
9898c2ecf20Sopenharmony_ci	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
9908c2ecf20Sopenharmony_ci		SET_U16_MSB(idtcm->channel[3].output_mask, val);
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	default:
9938c2ecf20Sopenharmony_ci		err = -EFAULT; /* Bad address */;
9948c2ecf20Sopenharmony_ci		break;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return err;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	if (index >= MAX_TOD) {
10038c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev, "ToD%d not supported\n", index);
10048c2ecf20Sopenharmony_ci		return -EINVAL;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (pll >= MAX_PLL) {
10088c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev, "Pll%d not supported\n", pll);
10098c2ecf20Sopenharmony_ci		return -EINVAL;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	idtcm->channel[index].pll = pll;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	return 0;
10158c2ecf20Sopenharmony_ci}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic int check_and_set_masks(struct idtcm *idtcm,
10188c2ecf20Sopenharmony_ci			       u16 regaddr,
10198c2ecf20Sopenharmony_ci			       u8 val)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	int err = 0;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	switch (regaddr) {
10248c2ecf20Sopenharmony_ci	case TOD_MASK_ADDR:
10258c2ecf20Sopenharmony_ci		if ((val & 0xf0) || !(val & 0x0f)) {
10268c2ecf20Sopenharmony_ci			dev_err(&idtcm->client->dev,
10278c2ecf20Sopenharmony_ci				"Invalid TOD mask 0x%hhx\n", val);
10288c2ecf20Sopenharmony_ci			err = -EINVAL;
10298c2ecf20Sopenharmony_ci		} else {
10308c2ecf20Sopenharmony_ci			idtcm->tod_mask = val;
10318c2ecf20Sopenharmony_ci		}
10328c2ecf20Sopenharmony_ci		break;
10338c2ecf20Sopenharmony_ci	case TOD0_PTP_PLL_ADDR:
10348c2ecf20Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 0, val);
10358c2ecf20Sopenharmony_ci		break;
10368c2ecf20Sopenharmony_ci	case TOD1_PTP_PLL_ADDR:
10378c2ecf20Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 1, val);
10388c2ecf20Sopenharmony_ci		break;
10398c2ecf20Sopenharmony_ci	case TOD2_PTP_PLL_ADDR:
10408c2ecf20Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 2, val);
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	case TOD3_PTP_PLL_ADDR:
10438c2ecf20Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 3, val);
10448c2ecf20Sopenharmony_ci		break;
10458c2ecf20Sopenharmony_ci	default:
10468c2ecf20Sopenharmony_ci		err = set_pll_output_mask(idtcm, regaddr, val);
10478c2ecf20Sopenharmony_ci		break;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return err;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic void display_pll_and_masks(struct idtcm *idtcm)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	u8 i;
10568c2ecf20Sopenharmony_ci	u8 mask;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x\n", idtcm->tod_mask);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TOD; i++) {
10618c2ecf20Sopenharmony_ci		mask = 1 << i;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci		if (mask & idtcm->tod_mask)
10648c2ecf20Sopenharmony_ci			dev_dbg(&idtcm->client->dev,
10658c2ecf20Sopenharmony_ci				"TOD%d pll = %d    output_mask = 0x%04x\n",
10668c2ecf20Sopenharmony_ci				i, idtcm->channel[i].pll,
10678c2ecf20Sopenharmony_ci				idtcm->channel[i].output_mask);
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic int idtcm_load_firmware(struct idtcm *idtcm,
10728c2ecf20Sopenharmony_ci			       struct device *dev)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	char fname[128] = FW_FILENAME;
10758c2ecf20Sopenharmony_ci	const struct firmware *fw;
10768c2ecf20Sopenharmony_ci	struct idtcm_fwrc *rec;
10778c2ecf20Sopenharmony_ci	u32 regaddr;
10788c2ecf20Sopenharmony_ci	int err;
10798c2ecf20Sopenharmony_ci	s32 len;
10808c2ecf20Sopenharmony_ci	u8 val;
10818c2ecf20Sopenharmony_ci	u8 loaddr;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (firmware) /* module parameter */
10848c2ecf20Sopenharmony_ci		snprintf(fname, sizeof(fname), "%s", firmware);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	dev_dbg(&idtcm->client->dev, "requesting firmware '%s'\n", fname);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	err = request_firmware(&fw, fname, dev);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	if (err) {
10918c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
10928c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
10938c2ecf20Sopenharmony_ci			__LINE__,
10948c2ecf20Sopenharmony_ci			__func__);
10958c2ecf20Sopenharmony_ci		return err;
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	dev_dbg(&idtcm->client->dev, "firmware size %zu bytes\n", fw->size);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	rec = (struct idtcm_fwrc *) fw->data;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (fw->size > 0)
11038c2ecf20Sopenharmony_ci		idtcm_state_machine_reset(idtcm);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		if (rec->reserved) {
11088c2ecf20Sopenharmony_ci			dev_err(&idtcm->client->dev,
11098c2ecf20Sopenharmony_ci				"bad firmware, reserved field non-zero\n");
11108c2ecf20Sopenharmony_ci			err = -EINVAL;
11118c2ecf20Sopenharmony_ci		} else {
11128c2ecf20Sopenharmony_ci			regaddr = rec->hiaddr << 8;
11138c2ecf20Sopenharmony_ci			regaddr |= rec->loaddr;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci			val = rec->value;
11168c2ecf20Sopenharmony_ci			loaddr = rec->loaddr;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci			rec++;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci			err = check_and_set_masks(idtcm, regaddr, val);
11218c2ecf20Sopenharmony_ci		}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		if (err != -EINVAL) {
11248c2ecf20Sopenharmony_ci			err = 0;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci			/* Top (status registers) and bottom are read-only */
11278c2ecf20Sopenharmony_ci			if ((regaddr < GPIO_USER_CONTROL)
11288c2ecf20Sopenharmony_ci			    || (regaddr >= SCRATCH))
11298c2ecf20Sopenharmony_ci				continue;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci			/* Page size 128, last 4 bytes of page skipped */
11328c2ecf20Sopenharmony_ci			if (((loaddr > 0x7b) && (loaddr <= 0x7f))
11338c2ecf20Sopenharmony_ci			     || loaddr > 0xfb)
11348c2ecf20Sopenharmony_ci				continue;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
11378c2ecf20Sopenharmony_ci		}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci		if (err)
11408c2ecf20Sopenharmony_ci			goto out;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	display_pll_and_masks(idtcm);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ciout:
11468c2ecf20Sopenharmony_ci	release_firmware(fw);
11478c2ecf20Sopenharmony_ci	return err;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int idtcm_output_enable(struct idtcm_channel *channel,
11518c2ecf20Sopenharmony_ci			       bool enable, unsigned int outn)
11528c2ecf20Sopenharmony_ci{
11538c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
11548c2ecf20Sopenharmony_ci	int err;
11558c2ecf20Sopenharmony_ci	u8 val;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
11588c2ecf20Sopenharmony_ci			 OUT_CTRL_1, &val, sizeof(val));
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	if (err)
11618c2ecf20Sopenharmony_ci		return err;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	if (enable)
11648c2ecf20Sopenharmony_ci		val |= SQUELCH_DISABLE;
11658c2ecf20Sopenharmony_ci	else
11668c2ecf20Sopenharmony_ci		val &= ~SQUELCH_DISABLE;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	return idtcm_write(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
11698c2ecf20Sopenharmony_ci			   OUT_CTRL_1, &val, sizeof(val));
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic int idtcm_output_mask_enable(struct idtcm_channel *channel,
11738c2ecf20Sopenharmony_ci				    bool enable)
11748c2ecf20Sopenharmony_ci{
11758c2ecf20Sopenharmony_ci	u16 mask;
11768c2ecf20Sopenharmony_ci	int err;
11778c2ecf20Sopenharmony_ci	u8 outn;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	mask = channel->output_mask;
11808c2ecf20Sopenharmony_ci	outn = 0;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	while (mask) {
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		if (mask & 0x1) {
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci			err = idtcm_output_enable(channel, enable, outn);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci			if (err)
11898c2ecf20Sopenharmony_ci				return err;
11908c2ecf20Sopenharmony_ci		}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci		mask >>= 0x1;
11938c2ecf20Sopenharmony_ci		outn++;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	return 0;
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic int idtcm_perout_enable(struct idtcm_channel *channel,
12008c2ecf20Sopenharmony_ci			       bool enable,
12018c2ecf20Sopenharmony_ci			       struct ptp_perout_request *perout)
12028c2ecf20Sopenharmony_ci{
12038c2ecf20Sopenharmony_ci	unsigned int flags = perout->flags;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	if (flags == PEROUT_ENABLE_OUTPUT_MASK)
12068c2ecf20Sopenharmony_ci		return idtcm_output_mask_enable(channel, enable);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* Enable/disable individual output instead */
12098c2ecf20Sopenharmony_ci	return idtcm_output_enable(channel, enable, perout->index);
12108c2ecf20Sopenharmony_ci}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic int idtcm_set_pll_mode(struct idtcm_channel *channel,
12138c2ecf20Sopenharmony_ci			      enum pll_mode pll_mode)
12148c2ecf20Sopenharmony_ci{
12158c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
12168c2ecf20Sopenharmony_ci	int err;
12178c2ecf20Sopenharmony_ci	u8 dpll_mode;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
12208c2ecf20Sopenharmony_ci			 &dpll_mode, sizeof(dpll_mode));
12218c2ecf20Sopenharmony_ci	if (err)
12228c2ecf20Sopenharmony_ci		return err;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	dpll_mode |= (pll_mode << PLL_MODE_SHIFT);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	channel->pll_mode = pll_mode;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE,
12318c2ecf20Sopenharmony_ci			  &dpll_mode, sizeof(dpll_mode));
12328c2ecf20Sopenharmony_ci	if (err)
12338c2ecf20Sopenharmony_ci		return err;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	return 0;
12368c2ecf20Sopenharmony_ci}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci/* PTP Hardware Clock interface */
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci/**
12418c2ecf20Sopenharmony_ci * @brief Maximum absolute value for write phase offset in picoseconds
12428c2ecf20Sopenharmony_ci *
12438c2ecf20Sopenharmony_ci * Destination signed register is 32-bit register in resolution of 50ps
12448c2ecf20Sopenharmony_ci *
12458c2ecf20Sopenharmony_ci * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
12468c2ecf20Sopenharmony_ci */
12478c2ecf20Sopenharmony_cistatic int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	int err;
12528c2ecf20Sopenharmony_ci	u8 i;
12538c2ecf20Sopenharmony_ci	u8 buf[4] = {0};
12548c2ecf20Sopenharmony_ci	s32 phase_50ps;
12558c2ecf20Sopenharmony_ci	s64 offset_ps;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		if (err)
12628c2ecf20Sopenharmony_ci			return err;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		channel->write_phase_ready = 0;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		ptp_schedule_worker(channel->ptp_clock,
12678c2ecf20Sopenharmony_ci				    msecs_to_jiffies(WR_PHASE_SETUP_MS));
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	if (!channel->write_phase_ready)
12718c2ecf20Sopenharmony_ci		delta_ns = 0;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	offset_ps = (s64)delta_ns * 1000;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	/*
12768c2ecf20Sopenharmony_ci	 * Check for 32-bit signed max * 50:
12778c2ecf20Sopenharmony_ci	 *
12788c2ecf20Sopenharmony_ci	 * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
12798c2ecf20Sopenharmony_ci	 */
12808c2ecf20Sopenharmony_ci	if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
12818c2ecf20Sopenharmony_ci		offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
12828c2ecf20Sopenharmony_ci	else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
12838c2ecf20Sopenharmony_ci		offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	phase_50ps = DIV_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
12888c2ecf20Sopenharmony_ci		buf[i] = phase_50ps & 0xff;
12898c2ecf20Sopenharmony_ci		phase_50ps >>= 8;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
12938c2ecf20Sopenharmony_ci			  buf, sizeof(buf));
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	return err;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
13018c2ecf20Sopenharmony_ci	u8 i;
13028c2ecf20Sopenharmony_ci	bool neg_adj = 0;
13038c2ecf20Sopenharmony_ci	int err;
13048c2ecf20Sopenharmony_ci	u8 buf[6] = {0};
13058c2ecf20Sopenharmony_ci	s64 fcw;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (channel->pll_mode  != PLL_MODE_WRITE_FREQUENCY) {
13088c2ecf20Sopenharmony_ci		err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
13098c2ecf20Sopenharmony_ci		if (err)
13108c2ecf20Sopenharmony_ci			return err;
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/*
13148c2ecf20Sopenharmony_ci	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
13158c2ecf20Sopenharmony_ci	 *
13168c2ecf20Sopenharmony_ci	 * adjfreq:
13178c2ecf20Sopenharmony_ci	 *       ppb * 10^9
13188c2ecf20Sopenharmony_ci	 * FCW = ----------
13198c2ecf20Sopenharmony_ci	 *          111
13208c2ecf20Sopenharmony_ci	 *
13218c2ecf20Sopenharmony_ci	 * adjfine:
13228c2ecf20Sopenharmony_ci	 *       ppm_16 * 5^12
13238c2ecf20Sopenharmony_ci	 * FCW = -------------
13248c2ecf20Sopenharmony_ci	 *         111 * 2^4
13258c2ecf20Sopenharmony_ci	 */
13268c2ecf20Sopenharmony_ci	if (scaled_ppm < 0) {
13278c2ecf20Sopenharmony_ci		neg_adj = 1;
13288c2ecf20Sopenharmony_ci		scaled_ppm = -scaled_ppm;
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
13328c2ecf20Sopenharmony_ci	fcw = scaled_ppm * 244140625ULL;
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	fcw = div_u64(fcw, 1776);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	if (neg_adj)
13378c2ecf20Sopenharmony_ci		fcw = -fcw;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
13408c2ecf20Sopenharmony_ci		buf[i] = fcw & 0xff;
13418c2ecf20Sopenharmony_ci		fcw >>= 8;
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
13458c2ecf20Sopenharmony_ci			  buf, sizeof(buf));
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	return err;
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
13538c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
13548c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
13558c2ecf20Sopenharmony_ci	int err;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	err = _idtcm_gettime(channel, ts);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	if (err)
13628c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
13638c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
13648c2ecf20Sopenharmony_ci			__LINE__,
13658c2ecf20Sopenharmony_ci			__func__);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	return err;
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic int idtcm_settime(struct ptp_clock_info *ptp,
13738c2ecf20Sopenharmony_ci			 const struct timespec64 *ts)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
13768c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
13778c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
13788c2ecf20Sopenharmony_ci	int err;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	err = _idtcm_settime(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	if (err)
13858c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
13868c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
13878c2ecf20Sopenharmony_ci			__LINE__,
13888c2ecf20Sopenharmony_ci			__func__);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	return err;
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_cistatic int idtcm_settime_v487(struct ptp_clock_info *ptp,
13968c2ecf20Sopenharmony_ci			 const struct timespec64 *ts)
13978c2ecf20Sopenharmony_ci{
13988c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
13998c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
14008c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
14018c2ecf20Sopenharmony_ci	int err;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	err = _idtcm_settime_v487(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	if (err)
14088c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
14098c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
14108c2ecf20Sopenharmony_ci			__LINE__,
14118c2ecf20Sopenharmony_ci			__func__);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	return err;
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
14218c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
14228c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
14238c2ecf20Sopenharmony_ci	int err;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	err = _idtcm_adjtime(channel, delta);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	if (err)
14308c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
14318c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
14328c2ecf20Sopenharmony_ci			__LINE__,
14338c2ecf20Sopenharmony_ci			__func__);
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	return err;
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
14438c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
14448c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
14458c2ecf20Sopenharmony_ci	struct timespec64 ts;
14468c2ecf20Sopenharmony_ci	enum scsr_tod_write_type_sel type;
14478c2ecf20Sopenharmony_ci	int err;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_V487) {
14508c2ecf20Sopenharmony_ci		err = idtcm_do_phase_pull_in(channel, delta, 0);
14518c2ecf20Sopenharmony_ci		if (err)
14528c2ecf20Sopenharmony_ci			dev_err(&idtcm->client->dev,
14538c2ecf20Sopenharmony_ci				"Failed at line %d in func %s!\n",
14548c2ecf20Sopenharmony_ci				__LINE__,
14558c2ecf20Sopenharmony_ci				__func__);
14568c2ecf20Sopenharmony_ci		return err;
14578c2ecf20Sopenharmony_ci	}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	if (delta >= 0) {
14608c2ecf20Sopenharmony_ci		ts = ns_to_timespec64(delta);
14618c2ecf20Sopenharmony_ci		type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
14628c2ecf20Sopenharmony_ci	} else {
14638c2ecf20Sopenharmony_ci		ts = ns_to_timespec64(-delta);
14648c2ecf20Sopenharmony_ci		type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
14658c2ecf20Sopenharmony_ci	}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	err = _idtcm_settime_v487(channel, &ts, type);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (err)
14728c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
14738c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
14748c2ecf20Sopenharmony_ci			__LINE__,
14758c2ecf20Sopenharmony_ci			__func__);
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	return err;
14808c2ecf20Sopenharmony_ci}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_cistatic int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
14858c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	int err;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	err = _idtcm_adjphase(channel, delta);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	if (err)
14968c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
14978c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
14988c2ecf20Sopenharmony_ci			__LINE__,
14998c2ecf20Sopenharmony_ci			__func__);
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	return err;
15048c2ecf20Sopenharmony_ci}
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_cistatic int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
15078c2ecf20Sopenharmony_ci{
15088c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
15098c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	int err;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	err = _idtcm_adjfine(channel, scaled_ppm);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (err)
15208c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
15218c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
15228c2ecf20Sopenharmony_ci			__LINE__,
15238c2ecf20Sopenharmony_ci			__func__);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	return err;
15288c2ecf20Sopenharmony_ci}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_cistatic int idtcm_enable(struct ptp_clock_info *ptp,
15318c2ecf20Sopenharmony_ci			struct ptp_clock_request *rq, int on)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	int err;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	struct idtcm_channel *channel =
15368c2ecf20Sopenharmony_ci		container_of(ptp, struct idtcm_channel, caps);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	switch (rq->type) {
15398c2ecf20Sopenharmony_ci	case PTP_CLK_REQ_PEROUT:
15408c2ecf20Sopenharmony_ci		if (!on) {
15418c2ecf20Sopenharmony_ci			err = idtcm_perout_enable(channel, false, &rq->perout);
15428c2ecf20Sopenharmony_ci			if (err)
15438c2ecf20Sopenharmony_ci				dev_err(&channel->idtcm->client->dev,
15448c2ecf20Sopenharmony_ci					"Failed at line %d in func %s!\n",
15458c2ecf20Sopenharmony_ci					__LINE__,
15468c2ecf20Sopenharmony_ci					__func__);
15478c2ecf20Sopenharmony_ci			return err;
15488c2ecf20Sopenharmony_ci		}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci		/* Only accept a 1-PPS aligned to the second. */
15518c2ecf20Sopenharmony_ci		if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
15528c2ecf20Sopenharmony_ci		    rq->perout.period.nsec)
15538c2ecf20Sopenharmony_ci			return -ERANGE;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		err = idtcm_perout_enable(channel, true, &rq->perout);
15568c2ecf20Sopenharmony_ci		if (err)
15578c2ecf20Sopenharmony_ci			dev_err(&channel->idtcm->client->dev,
15588c2ecf20Sopenharmony_ci				"Failed at line %d in func %s!\n",
15598c2ecf20Sopenharmony_ci				__LINE__,
15608c2ecf20Sopenharmony_ci				__func__);
15618c2ecf20Sopenharmony_ci		return err;
15628c2ecf20Sopenharmony_ci	default:
15638c2ecf20Sopenharmony_ci		break;
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
15678c2ecf20Sopenharmony_ci}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_cistatic int _enable_pll_tod_sync(struct idtcm *idtcm,
15708c2ecf20Sopenharmony_ci				u8 pll,
15718c2ecf20Sopenharmony_ci				u8 sync_src,
15728c2ecf20Sopenharmony_ci				u8 qn,
15738c2ecf20Sopenharmony_ci				u8 qn_plus_1)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	int err;
15768c2ecf20Sopenharmony_ci	u8 val;
15778c2ecf20Sopenharmony_ci	u16 dpll;
15788c2ecf20Sopenharmony_ci	u16 out0 = 0, out1 = 0;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	if ((qn == 0) && (qn_plus_1 == 0))
15818c2ecf20Sopenharmony_ci		return 0;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	switch (pll) {
15848c2ecf20Sopenharmony_ci	case 0:
15858c2ecf20Sopenharmony_ci		dpll = DPLL_0;
15868c2ecf20Sopenharmony_ci		if (qn)
15878c2ecf20Sopenharmony_ci			out0 = OUTPUT_0;
15888c2ecf20Sopenharmony_ci		if (qn_plus_1)
15898c2ecf20Sopenharmony_ci			out1 = OUTPUT_1;
15908c2ecf20Sopenharmony_ci		break;
15918c2ecf20Sopenharmony_ci	case 1:
15928c2ecf20Sopenharmony_ci		dpll = DPLL_1;
15938c2ecf20Sopenharmony_ci		if (qn)
15948c2ecf20Sopenharmony_ci			out0 = OUTPUT_2;
15958c2ecf20Sopenharmony_ci		if (qn_plus_1)
15968c2ecf20Sopenharmony_ci			out1 = OUTPUT_3;
15978c2ecf20Sopenharmony_ci		break;
15988c2ecf20Sopenharmony_ci	case 2:
15998c2ecf20Sopenharmony_ci		dpll = DPLL_2;
16008c2ecf20Sopenharmony_ci		if (qn)
16018c2ecf20Sopenharmony_ci			out0 = OUTPUT_4;
16028c2ecf20Sopenharmony_ci		if (qn_plus_1)
16038c2ecf20Sopenharmony_ci			out1 = OUTPUT_5;
16048c2ecf20Sopenharmony_ci		break;
16058c2ecf20Sopenharmony_ci	case 3:
16068c2ecf20Sopenharmony_ci		dpll = DPLL_3;
16078c2ecf20Sopenharmony_ci		if (qn)
16088c2ecf20Sopenharmony_ci			out0 = OUTPUT_6;
16098c2ecf20Sopenharmony_ci		if (qn_plus_1)
16108c2ecf20Sopenharmony_ci			out1 = OUTPUT_7;
16118c2ecf20Sopenharmony_ci		break;
16128c2ecf20Sopenharmony_ci	case 4:
16138c2ecf20Sopenharmony_ci		dpll = DPLL_4;
16148c2ecf20Sopenharmony_ci		if (qn)
16158c2ecf20Sopenharmony_ci			out0 = OUTPUT_8;
16168c2ecf20Sopenharmony_ci		break;
16178c2ecf20Sopenharmony_ci	case 5:
16188c2ecf20Sopenharmony_ci		dpll = DPLL_5;
16198c2ecf20Sopenharmony_ci		if (qn)
16208c2ecf20Sopenharmony_ci			out0 = OUTPUT_9;
16218c2ecf20Sopenharmony_ci		if (qn_plus_1)
16228c2ecf20Sopenharmony_ci			out1 = OUTPUT_8;
16238c2ecf20Sopenharmony_ci		break;
16248c2ecf20Sopenharmony_ci	case 6:
16258c2ecf20Sopenharmony_ci		dpll = DPLL_6;
16268c2ecf20Sopenharmony_ci		if (qn)
16278c2ecf20Sopenharmony_ci			out0 = OUTPUT_10;
16288c2ecf20Sopenharmony_ci		if (qn_plus_1)
16298c2ecf20Sopenharmony_ci			out1 = OUTPUT_11;
16308c2ecf20Sopenharmony_ci		break;
16318c2ecf20Sopenharmony_ci	case 7:
16328c2ecf20Sopenharmony_ci		dpll = DPLL_7;
16338c2ecf20Sopenharmony_ci		if (qn)
16348c2ecf20Sopenharmony_ci			out0 = OUTPUT_11;
16358c2ecf20Sopenharmony_ci		break;
16368c2ecf20Sopenharmony_ci	default:
16378c2ecf20Sopenharmony_ci		return -EINVAL;
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	/*
16418c2ecf20Sopenharmony_ci	 * Enable OUTPUT OUT_SYNC.
16428c2ecf20Sopenharmony_ci	 */
16438c2ecf20Sopenharmony_ci	if (out0) {
16448c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci		if (err)
16478c2ecf20Sopenharmony_ci			return err;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci		val &= ~OUT_SYNC_DISABLE;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val));
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci		if (err)
16548c2ecf20Sopenharmony_ci			return err;
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (out1) {
16588c2ecf20Sopenharmony_ci		err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci		if (err)
16618c2ecf20Sopenharmony_ci			return err;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		val &= ~OUT_SYNC_DISABLE;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci		err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val));
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci		if (err)
16688c2ecf20Sopenharmony_ci			return err;
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	/* enable dpll sync tod pps, must be set before dpll_mode */
16728c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
16738c2ecf20Sopenharmony_ci	if (err)
16748c2ecf20Sopenharmony_ci		return err;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT);
16778c2ecf20Sopenharmony_ci	val |= (sync_src << TOD_SYNC_SOURCE_SHIFT);
16788c2ecf20Sopenharmony_ci	val |= TOD_SYNC_EN;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val));
16818c2ecf20Sopenharmony_ci}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_cistatic int idtcm_enable_tod_sync(struct idtcm_channel *channel)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	u8 pll;
16888c2ecf20Sopenharmony_ci	u8 sync_src;
16898c2ecf20Sopenharmony_ci	u8 qn;
16908c2ecf20Sopenharmony_ci	u8 qn_plus_1;
16918c2ecf20Sopenharmony_ci	u8 cfg;
16928c2ecf20Sopenharmony_ci	int err = 0;
16938c2ecf20Sopenharmony_ci	u16 output_mask = channel->output_mask;
16948c2ecf20Sopenharmony_ci	u8 out8_mux = 0;
16958c2ecf20Sopenharmony_ci	u8 out11_mux = 0;
16968c2ecf20Sopenharmony_ci	u8 temp;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	/*
16998c2ecf20Sopenharmony_ci	 * set tod_out_sync_enable to 0.
17008c2ecf20Sopenharmony_ci	 */
17018c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
17028c2ecf20Sopenharmony_ci	if (err)
17038c2ecf20Sopenharmony_ci		return err;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	cfg &= ~TOD_OUT_SYNC_ENABLE;
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
17088c2ecf20Sopenharmony_ci	if (err)
17098c2ecf20Sopenharmony_ci		return err;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	switch (channel->tod_n) {
17128c2ecf20Sopenharmony_ci	case TOD_0:
17138c2ecf20Sopenharmony_ci		sync_src = 0;
17148c2ecf20Sopenharmony_ci		break;
17158c2ecf20Sopenharmony_ci	case TOD_1:
17168c2ecf20Sopenharmony_ci		sync_src = 1;
17178c2ecf20Sopenharmony_ci		break;
17188c2ecf20Sopenharmony_ci	case TOD_2:
17198c2ecf20Sopenharmony_ci		sync_src = 2;
17208c2ecf20Sopenharmony_ci		break;
17218c2ecf20Sopenharmony_ci	case TOD_3:
17228c2ecf20Sopenharmony_ci		sync_src = 3;
17238c2ecf20Sopenharmony_ci		break;
17248c2ecf20Sopenharmony_ci	default:
17258c2ecf20Sopenharmony_ci		return -EINVAL;
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
17298c2ecf20Sopenharmony_ci			 &temp, sizeof(temp));
17308c2ecf20Sopenharmony_ci	if (err)
17318c2ecf20Sopenharmony_ci		return err;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
17348c2ecf20Sopenharmony_ci	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
17358c2ecf20Sopenharmony_ci		out8_mux = 1;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
17388c2ecf20Sopenharmony_ci			 &temp, sizeof(temp));
17398c2ecf20Sopenharmony_ci	if (err)
17408c2ecf20Sopenharmony_ci		return err;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
17438c2ecf20Sopenharmony_ci	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
17448c2ecf20Sopenharmony_ci		out11_mux = 1;
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	for (pll = 0; pll < 8; pll++) {
17478c2ecf20Sopenharmony_ci		qn = 0;
17488c2ecf20Sopenharmony_ci		qn_plus_1 = 0;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci		if (pll < 4) {
17518c2ecf20Sopenharmony_ci			/* First 4 pll has 2 outputs */
17528c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
17538c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
17548c2ecf20Sopenharmony_ci			qn_plus_1 = output_mask & 0x1;
17558c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
17568c2ecf20Sopenharmony_ci		} else if (pll == 4) {
17578c2ecf20Sopenharmony_ci			if (out8_mux == 0) {
17588c2ecf20Sopenharmony_ci				qn = output_mask & 0x1;
17598c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
17608c2ecf20Sopenharmony_ci			}
17618c2ecf20Sopenharmony_ci		} else if (pll == 5) {
17628c2ecf20Sopenharmony_ci			if (out8_mux) {
17638c2ecf20Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
17648c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
17658c2ecf20Sopenharmony_ci			}
17668c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
17678c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
17688c2ecf20Sopenharmony_ci		} else if (pll == 6) {
17698c2ecf20Sopenharmony_ci			qn = output_mask & 0x1;
17708c2ecf20Sopenharmony_ci			output_mask = output_mask >> 1;
17718c2ecf20Sopenharmony_ci			if (out11_mux) {
17728c2ecf20Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
17738c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
17748c2ecf20Sopenharmony_ci			}
17758c2ecf20Sopenharmony_ci		} else if (pll == 7) {
17768c2ecf20Sopenharmony_ci			if (out11_mux == 0) {
17778c2ecf20Sopenharmony_ci				qn = output_mask & 0x1;
17788c2ecf20Sopenharmony_ci				output_mask = output_mask >> 1;
17798c2ecf20Sopenharmony_ci			}
17808c2ecf20Sopenharmony_ci		}
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci		if ((qn != 0) || (qn_plus_1 != 0))
17838c2ecf20Sopenharmony_ci			err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn,
17848c2ecf20Sopenharmony_ci					       qn_plus_1);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci		if (err)
17878c2ecf20Sopenharmony_ci			return err;
17888c2ecf20Sopenharmony_ci	}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	return err;
17918c2ecf20Sopenharmony_ci}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_cistatic int idtcm_enable_tod(struct idtcm_channel *channel)
17948c2ecf20Sopenharmony_ci{
17958c2ecf20Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
17968c2ecf20Sopenharmony_ci	struct timespec64 ts = {0, 0};
17978c2ecf20Sopenharmony_ci	u8 cfg;
17988c2ecf20Sopenharmony_ci	int err;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	/*
18018c2ecf20Sopenharmony_ci	 * Start the TOD clock ticking.
18028c2ecf20Sopenharmony_ci	 */
18038c2ecf20Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18048c2ecf20Sopenharmony_ci	if (err)
18058c2ecf20Sopenharmony_ci		return err;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	cfg |= TOD_ENABLE;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg));
18108c2ecf20Sopenharmony_ci	if (err)
18118c2ecf20Sopenharmony_ci		return err;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	return _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
18148c2ecf20Sopenharmony_ci}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_cistatic void idtcm_display_version_info(struct idtcm *idtcm)
18178c2ecf20Sopenharmony_ci{
18188c2ecf20Sopenharmony_ci	u8 major;
18198c2ecf20Sopenharmony_ci	u8 minor;
18208c2ecf20Sopenharmony_ci	u8 hotfix;
18218c2ecf20Sopenharmony_ci	u16 product_id;
18228c2ecf20Sopenharmony_ci	u8 hw_rev_id;
18238c2ecf20Sopenharmony_ci	u8 config_select;
18248c2ecf20Sopenharmony_ci	char *fmt = "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d\n";
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	idtcm_read_major_release(idtcm, &major);
18278c2ecf20Sopenharmony_ci	idtcm_read_minor_release(idtcm, &minor);
18288c2ecf20Sopenharmony_ci	idtcm_read_hotfix_release(idtcm, &hotfix);
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	idtcm_read_product_id(idtcm, &product_id);
18318c2ecf20Sopenharmony_ci	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
18368c2ecf20Sopenharmony_ci		 major, minor, hotfix);
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	dev_info(&idtcm->client->dev, fmt, major, minor, hotfix,
18398c2ecf20Sopenharmony_ci		 product_id, hw_rev_id, config_select);
18408c2ecf20Sopenharmony_ci}
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_cistatic const struct ptp_clock_info idtcm_caps_v487 = {
18438c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
18448c2ecf20Sopenharmony_ci	.max_adj	= 244000,
18458c2ecf20Sopenharmony_ci	.n_per_out	= 12,
18468c2ecf20Sopenharmony_ci	.adjphase	= &idtcm_adjphase,
18478c2ecf20Sopenharmony_ci	.adjfine	= &idtcm_adjfine,
18488c2ecf20Sopenharmony_ci	.adjtime	= &idtcm_adjtime_v487,
18498c2ecf20Sopenharmony_ci	.gettime64	= &idtcm_gettime,
18508c2ecf20Sopenharmony_ci	.settime64	= &idtcm_settime_v487,
18518c2ecf20Sopenharmony_ci	.enable		= &idtcm_enable,
18528c2ecf20Sopenharmony_ci	.do_aux_work	= &set_write_phase_ready,
18538c2ecf20Sopenharmony_ci};
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_cistatic const struct ptp_clock_info idtcm_caps = {
18568c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
18578c2ecf20Sopenharmony_ci	.max_adj	= 244000,
18588c2ecf20Sopenharmony_ci	.n_per_out	= 12,
18598c2ecf20Sopenharmony_ci	.adjphase	= &idtcm_adjphase,
18608c2ecf20Sopenharmony_ci	.adjfine	= &idtcm_adjfine,
18618c2ecf20Sopenharmony_ci	.adjtime	= &idtcm_adjtime,
18628c2ecf20Sopenharmony_ci	.gettime64	= &idtcm_gettime,
18638c2ecf20Sopenharmony_ci	.settime64	= &idtcm_settime,
18648c2ecf20Sopenharmony_ci	.enable		= &idtcm_enable,
18658c2ecf20Sopenharmony_ci	.do_aux_work	= &set_write_phase_ready,
18668c2ecf20Sopenharmony_ci};
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_cistatic int configure_channel_pll(struct idtcm_channel *channel)
18698c2ecf20Sopenharmony_ci{
18708c2ecf20Sopenharmony_ci	int err = 0;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	switch (channel->pll) {
18738c2ecf20Sopenharmony_ci	case 0:
18748c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_0;
18758c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_0;
18768c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_0;
18778c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_0;
18788c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_0;
18798c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
18808c2ecf20Sopenharmony_ci		break;
18818c2ecf20Sopenharmony_ci	case 1:
18828c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_1;
18838c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_1;
18848c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_1;
18858c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_1;
18868c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_1;
18878c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
18888c2ecf20Sopenharmony_ci		break;
18898c2ecf20Sopenharmony_ci	case 2:
18908c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_2;
18918c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_2;
18928c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_2;
18938c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_2;
18948c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_2;
18958c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
18968c2ecf20Sopenharmony_ci		break;
18978c2ecf20Sopenharmony_ci	case 3:
18988c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_3;
18998c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_3;
19008c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_3;
19018c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_3;
19028c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_3;
19038c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
19048c2ecf20Sopenharmony_ci		break;
19058c2ecf20Sopenharmony_ci	case 4:
19068c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_4;
19078c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_4;
19088c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_4;
19098c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_4;
19108c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_4;
19118c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
19128c2ecf20Sopenharmony_ci		break;
19138c2ecf20Sopenharmony_ci	case 5:
19148c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_5;
19158c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_5;
19168c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_5;
19178c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_5;
19188c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_5;
19198c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
19208c2ecf20Sopenharmony_ci		break;
19218c2ecf20Sopenharmony_ci	case 6:
19228c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_6;
19238c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_6;
19248c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_6;
19258c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_6;
19268c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_6;
19278c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
19288c2ecf20Sopenharmony_ci		break;
19298c2ecf20Sopenharmony_ci	case 7:
19308c2ecf20Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_7;
19318c2ecf20Sopenharmony_ci		channel->dpll_n = DPLL_7;
19328c2ecf20Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_7;
19338c2ecf20Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_7;
19348c2ecf20Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_7;
19358c2ecf20Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
19368c2ecf20Sopenharmony_ci		break;
19378c2ecf20Sopenharmony_ci	default:
19388c2ecf20Sopenharmony_ci		err = -EINVAL;
19398c2ecf20Sopenharmony_ci	}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	return err;
19428c2ecf20Sopenharmony_ci}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistatic int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
19458c2ecf20Sopenharmony_ci{
19468c2ecf20Sopenharmony_ci	struct idtcm_channel *channel;
19478c2ecf20Sopenharmony_ci	int err;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	if (!(index < MAX_TOD))
19508c2ecf20Sopenharmony_ci		return -EINVAL;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	channel = &idtcm->channel[index];
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	/* Set pll addresses */
19558c2ecf20Sopenharmony_ci	err = configure_channel_pll(channel);
19568c2ecf20Sopenharmony_ci	if (err)
19578c2ecf20Sopenharmony_ci		return err;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	/* Set tod addresses */
19608c2ecf20Sopenharmony_ci	switch (index) {
19618c2ecf20Sopenharmony_ci	case 0:
19628c2ecf20Sopenharmony_ci		channel->tod_read_primary = TOD_READ_PRIMARY_0;
19638c2ecf20Sopenharmony_ci		channel->tod_write = TOD_WRITE_0;
19648c2ecf20Sopenharmony_ci		channel->tod_n = TOD_0;
19658c2ecf20Sopenharmony_ci		break;
19668c2ecf20Sopenharmony_ci	case 1:
19678c2ecf20Sopenharmony_ci		channel->tod_read_primary = TOD_READ_PRIMARY_1;
19688c2ecf20Sopenharmony_ci		channel->tod_write = TOD_WRITE_1;
19698c2ecf20Sopenharmony_ci		channel->tod_n = TOD_1;
19708c2ecf20Sopenharmony_ci		break;
19718c2ecf20Sopenharmony_ci	case 2:
19728c2ecf20Sopenharmony_ci		channel->tod_read_primary = TOD_READ_PRIMARY_2;
19738c2ecf20Sopenharmony_ci		channel->tod_write = TOD_WRITE_2;
19748c2ecf20Sopenharmony_ci		channel->tod_n = TOD_2;
19758c2ecf20Sopenharmony_ci		break;
19768c2ecf20Sopenharmony_ci	case 3:
19778c2ecf20Sopenharmony_ci		channel->tod_read_primary = TOD_READ_PRIMARY_3;
19788c2ecf20Sopenharmony_ci		channel->tod_write = TOD_WRITE_3;
19798c2ecf20Sopenharmony_ci		channel->tod_n = TOD_3;
19808c2ecf20Sopenharmony_ci		break;
19818c2ecf20Sopenharmony_ci	default:
19828c2ecf20Sopenharmony_ci		return -EINVAL;
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	channel->idtcm = idtcm;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
19888c2ecf20Sopenharmony_ci		channel->caps = idtcm_caps_v487;
19898c2ecf20Sopenharmony_ci	else
19908c2ecf20Sopenharmony_ci		channel->caps = idtcm_caps;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	snprintf(channel->caps.name, sizeof(channel->caps.name),
19938c2ecf20Sopenharmony_ci		 "IDT CM TOD%u", index);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) {
19968c2ecf20Sopenharmony_ci		err = idtcm_enable_tod_sync(channel);
19978c2ecf20Sopenharmony_ci		if (err) {
19988c2ecf20Sopenharmony_ci			dev_err(&idtcm->client->dev,
19998c2ecf20Sopenharmony_ci				"Failed at line %d in func %s!\n",
20008c2ecf20Sopenharmony_ci				__LINE__,
20018c2ecf20Sopenharmony_ci				__func__);
20028c2ecf20Sopenharmony_ci			return err;
20038c2ecf20Sopenharmony_ci		}
20048c2ecf20Sopenharmony_ci	}
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
20078c2ecf20Sopenharmony_ci	if (err) {
20088c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
20098c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
20108c2ecf20Sopenharmony_ci			__LINE__,
20118c2ecf20Sopenharmony_ci			__func__);
20128c2ecf20Sopenharmony_ci		return err;
20138c2ecf20Sopenharmony_ci	}
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	err = idtcm_enable_tod(channel);
20168c2ecf20Sopenharmony_ci	if (err) {
20178c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
20188c2ecf20Sopenharmony_ci			"Failed at line %d in func %s!\n",
20198c2ecf20Sopenharmony_ci			__LINE__,
20208c2ecf20Sopenharmony_ci			__func__);
20218c2ecf20Sopenharmony_ci		return err;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	if (IS_ERR(channel->ptp_clock)) {
20278c2ecf20Sopenharmony_ci		err = PTR_ERR(channel->ptp_clock);
20288c2ecf20Sopenharmony_ci		channel->ptp_clock = NULL;
20298c2ecf20Sopenharmony_ci		return err;
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	if (!channel->ptp_clock)
20338c2ecf20Sopenharmony_ci		return -ENOTSUPP;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	channel->write_phase_ready = 0;
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
20388c2ecf20Sopenharmony_ci		 index, channel->ptp_clock->index);
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	return 0;
20418c2ecf20Sopenharmony_ci}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_cistatic void ptp_clock_unregister_all(struct idtcm *idtcm)
20448c2ecf20Sopenharmony_ci{
20458c2ecf20Sopenharmony_ci	u8 i;
20468c2ecf20Sopenharmony_ci	struct idtcm_channel *channel;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TOD; i++) {
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci		channel = &idtcm->channel[i];
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci		if (channel->ptp_clock)
20538c2ecf20Sopenharmony_ci			ptp_clock_unregister(channel->ptp_clock);
20548c2ecf20Sopenharmony_ci	}
20558c2ecf20Sopenharmony_ci}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_cistatic void set_default_masks(struct idtcm *idtcm)
20588c2ecf20Sopenharmony_ci{
20598c2ecf20Sopenharmony_ci	idtcm->tod_mask = DEFAULT_TOD_MASK;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
20628c2ecf20Sopenharmony_ci	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
20638c2ecf20Sopenharmony_ci	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
20648c2ecf20Sopenharmony_ci	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
20678c2ecf20Sopenharmony_ci	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
20688c2ecf20Sopenharmony_ci	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
20698c2ecf20Sopenharmony_ci	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_cistatic int idtcm_probe(struct i2c_client *client,
20738c2ecf20Sopenharmony_ci		       const struct i2c_device_id *id)
20748c2ecf20Sopenharmony_ci{
20758c2ecf20Sopenharmony_ci	struct idtcm *idtcm;
20768c2ecf20Sopenharmony_ci	int err;
20778c2ecf20Sopenharmony_ci	u8 i;
20788c2ecf20Sopenharmony_ci	char *fmt = "Failed at %d in line %s with channel output %d!\n";
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	/* Unused for now */
20818c2ecf20Sopenharmony_ci	(void)id;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL);
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	if (!idtcm)
20868c2ecf20Sopenharmony_ci		return -ENOMEM;
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	idtcm->client = client;
20898c2ecf20Sopenharmony_ci	idtcm->page_offset = 0xff;
20908c2ecf20Sopenharmony_ci	idtcm->calculate_overhead_flag = 0;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	set_default_masks(idtcm);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	mutex_init(&idtcm->reg_lock);
20958c2ecf20Sopenharmony_ci	mutex_lock(&idtcm->reg_lock);
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	idtcm_display_version_info(idtcm);
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	err = idtcm_load_firmware(idtcm, &client->dev);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	if (err)
21028c2ecf20Sopenharmony_ci		dev_warn(&idtcm->client->dev,
21038c2ecf20Sopenharmony_ci			 "loading firmware failed with %d\n", err);
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	if (idtcm->tod_mask) {
21068c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_TOD; i++) {
21078c2ecf20Sopenharmony_ci			if (idtcm->tod_mask & (1 << i)) {
21088c2ecf20Sopenharmony_ci				err = idtcm_enable_channel(idtcm, i);
21098c2ecf20Sopenharmony_ci				if (err) {
21108c2ecf20Sopenharmony_ci					dev_err(&idtcm->client->dev,
21118c2ecf20Sopenharmony_ci						fmt,
21128c2ecf20Sopenharmony_ci						__LINE__,
21138c2ecf20Sopenharmony_ci						__func__,
21148c2ecf20Sopenharmony_ci						i);
21158c2ecf20Sopenharmony_ci					break;
21168c2ecf20Sopenharmony_ci				}
21178c2ecf20Sopenharmony_ci			}
21188c2ecf20Sopenharmony_ci		}
21198c2ecf20Sopenharmony_ci	} else {
21208c2ecf20Sopenharmony_ci		dev_err(&idtcm->client->dev,
21218c2ecf20Sopenharmony_ci			"no PLLs flagged as PHCs, nothing to do\n");
21228c2ecf20Sopenharmony_ci		err = -ENODEV;
21238c2ecf20Sopenharmony_ci	}
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	mutex_unlock(&idtcm->reg_lock);
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (err) {
21288c2ecf20Sopenharmony_ci		ptp_clock_unregister_all(idtcm);
21298c2ecf20Sopenharmony_ci		return err;
21308c2ecf20Sopenharmony_ci	}
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, idtcm);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	return 0;
21358c2ecf20Sopenharmony_ci}
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_cistatic int idtcm_remove(struct i2c_client *client)
21388c2ecf20Sopenharmony_ci{
21398c2ecf20Sopenharmony_ci	struct idtcm *idtcm = i2c_get_clientdata(client);
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	ptp_clock_unregister_all(idtcm);
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	mutex_destroy(&idtcm->reg_lock);
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	return 0;
21468c2ecf20Sopenharmony_ci}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
21498c2ecf20Sopenharmony_cistatic const struct of_device_id idtcm_dt_id[] = {
21508c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34000" },
21518c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34001" },
21528c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34002" },
21538c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34003" },
21548c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34004" },
21558c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34005" },
21568c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34006" },
21578c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34007" },
21588c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34008" },
21598c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34009" },
21608c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34010" },
21618c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34011" },
21628c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34012" },
21638c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34013" },
21648c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34014" },
21658c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34015" },
21668c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34016" },
21678c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34017" },
21688c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34018" },
21698c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34019" },
21708c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34040" },
21718c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34041" },
21728c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34042" },
21738c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34043" },
21748c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34044" },
21758c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34045" },
21768c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34046" },
21778c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34047" },
21788c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34048" },
21798c2ecf20Sopenharmony_ci	{ .compatible = "idt,8a34049" },
21808c2ecf20Sopenharmony_ci	{},
21818c2ecf20Sopenharmony_ci};
21828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, idtcm_dt_id);
21838c2ecf20Sopenharmony_ci#endif
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_cistatic const struct i2c_device_id idtcm_i2c_id[] = {
21868c2ecf20Sopenharmony_ci	{ "8a34000" },
21878c2ecf20Sopenharmony_ci	{ "8a34001" },
21888c2ecf20Sopenharmony_ci	{ "8a34002" },
21898c2ecf20Sopenharmony_ci	{ "8a34003" },
21908c2ecf20Sopenharmony_ci	{ "8a34004" },
21918c2ecf20Sopenharmony_ci	{ "8a34005" },
21928c2ecf20Sopenharmony_ci	{ "8a34006" },
21938c2ecf20Sopenharmony_ci	{ "8a34007" },
21948c2ecf20Sopenharmony_ci	{ "8a34008" },
21958c2ecf20Sopenharmony_ci	{ "8a34009" },
21968c2ecf20Sopenharmony_ci	{ "8a34010" },
21978c2ecf20Sopenharmony_ci	{ "8a34011" },
21988c2ecf20Sopenharmony_ci	{ "8a34012" },
21998c2ecf20Sopenharmony_ci	{ "8a34013" },
22008c2ecf20Sopenharmony_ci	{ "8a34014" },
22018c2ecf20Sopenharmony_ci	{ "8a34015" },
22028c2ecf20Sopenharmony_ci	{ "8a34016" },
22038c2ecf20Sopenharmony_ci	{ "8a34017" },
22048c2ecf20Sopenharmony_ci	{ "8a34018" },
22058c2ecf20Sopenharmony_ci	{ "8a34019" },
22068c2ecf20Sopenharmony_ci	{ "8a34040" },
22078c2ecf20Sopenharmony_ci	{ "8a34041" },
22088c2ecf20Sopenharmony_ci	{ "8a34042" },
22098c2ecf20Sopenharmony_ci	{ "8a34043" },
22108c2ecf20Sopenharmony_ci	{ "8a34044" },
22118c2ecf20Sopenharmony_ci	{ "8a34045" },
22128c2ecf20Sopenharmony_ci	{ "8a34046" },
22138c2ecf20Sopenharmony_ci	{ "8a34047" },
22148c2ecf20Sopenharmony_ci	{ "8a34048" },
22158c2ecf20Sopenharmony_ci	{ "8a34049" },
22168c2ecf20Sopenharmony_ci	{},
22178c2ecf20Sopenharmony_ci};
22188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, idtcm_i2c_id);
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_cistatic struct i2c_driver idtcm_driver = {
22218c2ecf20Sopenharmony_ci	.driver = {
22228c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(idtcm_dt_id),
22238c2ecf20Sopenharmony_ci		.name		= "idtcm",
22248c2ecf20Sopenharmony_ci	},
22258c2ecf20Sopenharmony_ci	.probe		= idtcm_probe,
22268c2ecf20Sopenharmony_ci	.remove		= idtcm_remove,
22278c2ecf20Sopenharmony_ci	.id_table	= idtcm_i2c_id,
22288c2ecf20Sopenharmony_ci};
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_cimodule_i2c_driver(idtcm_driver);
2231