162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PTP hardware clock driver for the IDT ClockMatrix(TM) family of timing and
462306a36Sopenharmony_ci * synchronization devices.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/firmware.h>
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/timekeeping.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/mfd/rsmu.h>
1962306a36Sopenharmony_ci#include <linux/mfd/idt8a340_reg.h>
2062306a36Sopenharmony_ci#include <asm/unaligned.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "ptp_private.h"
2362306a36Sopenharmony_ci#include "ptp_clockmatrix.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family");
2662306a36Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
2762306a36Sopenharmony_ciMODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
2862306a36Sopenharmony_ciMODULE_VERSION("1.0");
2962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * The name of the firmware file to be loaded
3362306a36Sopenharmony_ci * over-rides any automatic selection
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic char *firmware;
3662306a36Sopenharmony_cimodule_param(firmware, charp, 0);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define SETTIME_CORRECTION (0)
3962306a36Sopenharmony_ci#define EXTTS_PERIOD_MS (95)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline int idtcm_read(struct idtcm *idtcm,
4462306a36Sopenharmony_ci			     u16 module,
4562306a36Sopenharmony_ci			     u16 regaddr,
4662306a36Sopenharmony_ci			     u8 *buf,
4762306a36Sopenharmony_ci			     u16 count)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return regmap_bulk_read(idtcm->regmap, module + regaddr, buf, count);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic inline int idtcm_write(struct idtcm *idtcm,
5362306a36Sopenharmony_ci			      u16 module,
5462306a36Sopenharmony_ci			      u16 regaddr,
5562306a36Sopenharmony_ci			      u8 *buf,
5662306a36Sopenharmony_ci			      u16 count)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return regmap_bulk_write(idtcm->regmap, module + regaddr, buf, count);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int contains_full_configuration(struct idtcm *idtcm,
6262306a36Sopenharmony_ci				       const struct firmware *fw)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data;
6562306a36Sopenharmony_ci	u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH);
6662306a36Sopenharmony_ci	s32 full_count;
6762306a36Sopenharmony_ci	s32 count = 0;
6862306a36Sopenharmony_ci	u16 regaddr;
6962306a36Sopenharmony_ci	u8 loaddr;
7062306a36Sopenharmony_ci	s32 len;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* 4 bytes skipped every 0x80 */
7362306a36Sopenharmony_ci	full_count = (scratch - GPIO_USER_CONTROL) -
7462306a36Sopenharmony_ci		     ((scratch >> 7) - (GPIO_USER_CONTROL >> 7)) * 4;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* If the firmware contains 'full configuration' SM_RESET can be used
7762306a36Sopenharmony_ci	 * to ensure proper configuration.
7862306a36Sopenharmony_ci	 *
7962306a36Sopenharmony_ci	 * Full configuration is defined as the number of programmable
8062306a36Sopenharmony_ci	 * bytes within the configuration range minus page offset addr range.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
8362306a36Sopenharmony_ci		regaddr = rec->hiaddr << 8;
8462306a36Sopenharmony_ci		regaddr |= rec->loaddr;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		loaddr = rec->loaddr;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		rec++;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		/* Top (status registers) and bottom are read-only */
9162306a36Sopenharmony_ci		if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch)
9262306a36Sopenharmony_ci			continue;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		/* Page size 128, last 4 bytes of page skipped */
9562306a36Sopenharmony_ci		if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
9662306a36Sopenharmony_ci			continue;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		count++;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return (count >= full_count);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int char_array_to_timespec(u8 *buf,
10562306a36Sopenharmony_ci				  u8 count,
10662306a36Sopenharmony_ci				  struct timespec64 *ts)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	u8 i;
10962306a36Sopenharmony_ci	u64 nsec;
11062306a36Sopenharmony_ci	time64_t sec;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (count < TOD_BYTE_COUNT)
11362306a36Sopenharmony_ci		return 1;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Sub-nanoseconds are in buf[0]. */
11662306a36Sopenharmony_ci	nsec = buf[4];
11762306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
11862306a36Sopenharmony_ci		nsec <<= 8;
11962306a36Sopenharmony_ci		nsec |= buf[3 - i];
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	sec = buf[10];
12362306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
12462306a36Sopenharmony_ci		sec <<= 8;
12562306a36Sopenharmony_ci		sec |= buf[9 - i];
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ts->tv_sec = sec;
12962306a36Sopenharmony_ci	ts->tv_nsec = nsec;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int timespec_to_char_array(struct timespec64 const *ts,
13562306a36Sopenharmony_ci				  u8 *buf,
13662306a36Sopenharmony_ci				  u8 count)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u8 i;
13962306a36Sopenharmony_ci	s32 nsec;
14062306a36Sopenharmony_ci	time64_t sec;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (count < TOD_BYTE_COUNT)
14362306a36Sopenharmony_ci		return 1;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	nsec = ts->tv_nsec;
14662306a36Sopenharmony_ci	sec = ts->tv_sec;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Sub-nanoseconds are in buf[0]. */
14962306a36Sopenharmony_ci	buf[0] = 0;
15062306a36Sopenharmony_ci	for (i = 1; i < 5; i++) {
15162306a36Sopenharmony_ci		buf[i] = nsec & 0xff;
15262306a36Sopenharmony_ci		nsec >>= 8;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 5; i < TOD_BYTE_COUNT; i++) {
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		buf[i] = sec & 0xff;
15862306a36Sopenharmony_ci		sec >>= 8;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int idtcm_strverscmp(const char *version1, const char *version2)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u8 ver1[3], ver2[3];
16762306a36Sopenharmony_ci	int i;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (sscanf(version1, "%hhu.%hhu.%hhu",
17062306a36Sopenharmony_ci		   &ver1[0], &ver1[1], &ver1[2]) != 3)
17162306a36Sopenharmony_ci		return -1;
17262306a36Sopenharmony_ci	if (sscanf(version2, "%hhu.%hhu.%hhu",
17362306a36Sopenharmony_ci		   &ver2[0], &ver2[1], &ver2[2]) != 3)
17462306a36Sopenharmony_ci		return -1;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
17762306a36Sopenharmony_ci		if (ver1[i] > ver2[i])
17862306a36Sopenharmony_ci			return 1;
17962306a36Sopenharmony_ci		if (ver1[i] < ver2[i])
18062306a36Sopenharmony_ci			return -1;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic enum fw_version idtcm_fw_version(const char *version)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	enum fw_version ver = V_DEFAULT;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (idtcm_strverscmp(version, "4.8.7") >= 0)
19162306a36Sopenharmony_ci		ver = V487;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (idtcm_strverscmp(version, "5.2.0") >= 0)
19462306a36Sopenharmony_ci		ver = V520;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return ver;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int clear_boot_status(struct idtcm *idtcm)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	u8 buf[4] = {0};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int read_boot_status(struct idtcm *idtcm, u32 *status)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int err;
20962306a36Sopenharmony_ci	u8 buf[4] = {0};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	*status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return err;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int wait_for_boot_status_ready(struct idtcm *idtcm)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	u32 status = 0;
22162306a36Sopenharmony_ci	u8 i = 30;	/* 30 * 100ms = 3s */
22262306a36Sopenharmony_ci	int err;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	do {
22562306a36Sopenharmony_ci		err = read_boot_status(idtcm, &status);
22662306a36Sopenharmony_ci		if (err)
22762306a36Sopenharmony_ci			return err;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if (status == 0xA0)
23062306a36Sopenharmony_ci			return 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		msleep(100);
23362306a36Sopenharmony_ci		i--;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	} while (i);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	dev_warn(idtcm->dev, "%s timed out", __func__);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return -EBUSY;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int arm_tod_read_trig_sel_refclk(struct idtcm_channel *channel, u8 ref)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
24562306a36Sopenharmony_ci	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
24662306a36Sopenharmony_ci	u8 val = 0;
24762306a36Sopenharmony_ci	int err;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
25062306a36Sopenharmony_ci	val |= (ref << WR_REF_INDEX_SHIFT);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_read_secondary,
25362306a36Sopenharmony_ci			  TOD_READ_SECONDARY_SEL_CFG_0, &val, sizeof(val));
25462306a36Sopenharmony_ci	if (err)
25562306a36Sopenharmony_ci		return err;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	val = 0 | (SCSR_TOD_READ_TRIG_SEL_REFCLK << TOD_READ_TRIGGER_SHIFT);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_read_secondary, tod_read_cmd,
26062306a36Sopenharmony_ci			  &val, sizeof(val));
26162306a36Sopenharmony_ci	if (err)
26262306a36Sopenharmony_ci		dev_err(idtcm->dev, "%s: err = %d", __func__, err);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return err;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic bool is_single_shot(u8 mask)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	/* Treat single bit ToD masks as continuous trigger */
27062306a36Sopenharmony_ci	return !(mask <= 8 && is_power_of_2(mask));
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int idtcm_extts_enable(struct idtcm_channel *channel,
27462306a36Sopenharmony_ci			      struct ptp_clock_request *rq, int on)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	u8 index = rq->extts.index;
27762306a36Sopenharmony_ci	struct idtcm *idtcm;
27862306a36Sopenharmony_ci	u8 mask = 1 << index;
27962306a36Sopenharmony_ci	int err = 0;
28062306a36Sopenharmony_ci	u8 old_mask;
28162306a36Sopenharmony_ci	int ref;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	idtcm = channel->idtcm;
28462306a36Sopenharmony_ci	old_mask = idtcm->extts_mask;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Reject requests with unsupported flags */
28762306a36Sopenharmony_ci	if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
28862306a36Sopenharmony_ci				PTP_RISING_EDGE |
28962306a36Sopenharmony_ci				PTP_FALLING_EDGE |
29062306a36Sopenharmony_ci				PTP_STRICT_FLAGS))
29162306a36Sopenharmony_ci		return -EOPNOTSUPP;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Reject requests to enable time stamping on falling edge */
29462306a36Sopenharmony_ci	if ((rq->extts.flags & PTP_ENABLE_FEATURE) &&
29562306a36Sopenharmony_ci	    (rq->extts.flags & PTP_FALLING_EDGE))
29662306a36Sopenharmony_ci		return -EOPNOTSUPP;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (index >= MAX_TOD)
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (on) {
30262306a36Sopenharmony_ci		/* Support triggering more than one TOD_0/1/2/3 by same pin */
30362306a36Sopenharmony_ci		/* Use the pin configured for the channel */
30462306a36Sopenharmony_ci		ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->tod);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		if (ref < 0) {
30762306a36Sopenharmony_ci			dev_err(idtcm->dev, "%s: No valid pin found for TOD%d!\n",
30862306a36Sopenharmony_ci				__func__, channel->tod);
30962306a36Sopenharmony_ci			return -EBUSY;
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		err = arm_tod_read_trig_sel_refclk(&idtcm->channel[index], ref);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (err == 0) {
31562306a36Sopenharmony_ci			idtcm->extts_mask |= mask;
31662306a36Sopenharmony_ci			idtcm->event_channel[index] = channel;
31762306a36Sopenharmony_ci			idtcm->channel[index].refn = ref;
31862306a36Sopenharmony_ci			idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			if (old_mask)
32162306a36Sopenharmony_ci				return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci			schedule_delayed_work(&idtcm->extts_work,
32462306a36Sopenharmony_ci					      msecs_to_jiffies(EXTTS_PERIOD_MS));
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci	} else {
32762306a36Sopenharmony_ci		idtcm->extts_mask &= ~mask;
32862306a36Sopenharmony_ci		idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		if (idtcm->extts_mask == 0)
33162306a36Sopenharmony_ci			cancel_delayed_work(&idtcm->extts_work);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return err;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int read_sys_apll_status(struct idtcm *idtcm, u8 *status)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status,
34062306a36Sopenharmony_ci			  sizeof(u8));
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int read_sys_dpll_status(struct idtcm *idtcm, u8 *status)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8));
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS);
35162306a36Sopenharmony_ci	u8 apll = 0;
35262306a36Sopenharmony_ci	u8 dpll = 0;
35362306a36Sopenharmony_ci	int err;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	do {
35662306a36Sopenharmony_ci		err = read_sys_apll_status(idtcm, &apll);
35762306a36Sopenharmony_ci		if (err)
35862306a36Sopenharmony_ci			return err;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		err = read_sys_dpll_status(idtcm, &dpll);
36162306a36Sopenharmony_ci		if (err)
36262306a36Sopenharmony_ci			return err;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK;
36562306a36Sopenharmony_ci		dpll &= DPLL_SYS_STATE_MASK;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED &&
36862306a36Sopenharmony_ci		    dpll == DPLL_STATE_LOCKED) {
36962306a36Sopenharmony_ci			return 0;
37062306a36Sopenharmony_ci		} else if (dpll == DPLL_STATE_FREERUN ||
37162306a36Sopenharmony_ci			   dpll == DPLL_STATE_HOLDOVER ||
37262306a36Sopenharmony_ci			   dpll == DPLL_STATE_OPEN_LOOP) {
37362306a36Sopenharmony_ci			dev_warn(idtcm->dev,
37462306a36Sopenharmony_ci				"No wait state: DPLL_SYS_STATE %d", dpll);
37562306a36Sopenharmony_ci			return -EPERM;
37662306a36Sopenharmony_ci		}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		msleep(LOCK_POLL_INTERVAL_MS);
37962306a36Sopenharmony_ci	} while (time_is_after_jiffies(timeout));
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	dev_warn(idtcm->dev,
38262306a36Sopenharmony_ci		 "%d ms lock timeout: SYS APLL Loss Lock %d  SYS DPLL state %d",
38362306a36Sopenharmony_ci		 LOCK_TIMEOUT_MS, apll, dpll);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return -ETIME;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void wait_for_chip_ready(struct idtcm *idtcm)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	if (wait_for_boot_status_ready(idtcm))
39162306a36Sopenharmony_ci		dev_warn(idtcm->dev, "BOOT_STATUS != 0xA0");
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (wait_for_sys_apll_dpll_lock(idtcm))
39462306a36Sopenharmony_ci		dev_warn(idtcm->dev,
39562306a36Sopenharmony_ci			 "Continuing while SYS APLL/DPLL is not locked");
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int _idtcm_gettime_triggered(struct idtcm_channel *channel,
39962306a36Sopenharmony_ci				    struct timespec64 *ts)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
40262306a36Sopenharmony_ci	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
40362306a36Sopenharmony_ci	u8 buf[TOD_BYTE_COUNT];
40462306a36Sopenharmony_ci	u8 trigger;
40562306a36Sopenharmony_ci	int err;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_read_secondary,
40862306a36Sopenharmony_ci			 tod_read_cmd, &trigger, sizeof(trigger));
40962306a36Sopenharmony_ci	if (err)
41062306a36Sopenharmony_ci		return err;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (trigger & TOD_READ_TRIGGER_MASK)
41362306a36Sopenharmony_ci		return -EBUSY;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_read_secondary,
41662306a36Sopenharmony_ci			 TOD_READ_SECONDARY_BASE, buf, sizeof(buf));
41762306a36Sopenharmony_ci	if (err)
41862306a36Sopenharmony_ci		return err;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return char_array_to_timespec(buf, sizeof(buf), ts);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int _idtcm_gettime(struct idtcm_channel *channel,
42462306a36Sopenharmony_ci			  struct timespec64 *ts, u8 timeout)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
42762306a36Sopenharmony_ci	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
42862306a36Sopenharmony_ci	u8 buf[TOD_BYTE_COUNT];
42962306a36Sopenharmony_ci	u8 trigger;
43062306a36Sopenharmony_ci	int err;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* wait trigger to be 0 */
43362306a36Sopenharmony_ci	do {
43462306a36Sopenharmony_ci		if (timeout-- == 0)
43562306a36Sopenharmony_ci			return -EIO;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (idtcm->calculate_overhead_flag)
43862306a36Sopenharmony_ci			idtcm->start_time = ktime_get_raw();
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		err = idtcm_read(idtcm, channel->tod_read_primary,
44162306a36Sopenharmony_ci				 tod_read_cmd, &trigger,
44262306a36Sopenharmony_ci				 sizeof(trigger));
44362306a36Sopenharmony_ci		if (err)
44462306a36Sopenharmony_ci			return err;
44562306a36Sopenharmony_ci	} while (trigger & TOD_READ_TRIGGER_MASK);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_read_primary,
44862306a36Sopenharmony_ci			 TOD_READ_PRIMARY_BASE, buf, sizeof(buf));
44962306a36Sopenharmony_ci	if (err)
45062306a36Sopenharmony_ci		return err;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	err = char_array_to_timespec(buf, sizeof(buf), ts);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return err;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int idtcm_extts_check_channel(struct idtcm *idtcm, u8 todn)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct idtcm_channel *ptp_channel, *extts_channel;
46062306a36Sopenharmony_ci	struct ptp_clock_event event;
46162306a36Sopenharmony_ci	struct timespec64 ts;
46262306a36Sopenharmony_ci	u32 dco_delay = 0;
46362306a36Sopenharmony_ci	int err;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	extts_channel = &idtcm->channel[todn];
46662306a36Sopenharmony_ci	ptp_channel = idtcm->event_channel[todn];
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (extts_channel == ptp_channel)
46962306a36Sopenharmony_ci		dco_delay = ptp_channel->dco_delay;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	err = _idtcm_gettime_triggered(extts_channel, &ts);
47262306a36Sopenharmony_ci	if (err)
47362306a36Sopenharmony_ci		return err;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Triggered - save timestamp */
47662306a36Sopenharmony_ci	event.type = PTP_CLOCK_EXTTS;
47762306a36Sopenharmony_ci	event.index = todn;
47862306a36Sopenharmony_ci	event.timestamp = timespec64_to_ns(&ts) - dco_delay;
47962306a36Sopenharmony_ci	ptp_clock_event(ptp_channel->ptp_clock, &event);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return err;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int _idtcm_gettime_immediate(struct idtcm_channel *channel,
48562306a36Sopenharmony_ci				    struct timespec64 *ts)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
49062306a36Sopenharmony_ci	u8 val = (SCSR_TOD_READ_TRIG_SEL_IMMEDIATE << TOD_READ_TRIGGER_SHIFT);
49162306a36Sopenharmony_ci	int err;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_read_primary,
49462306a36Sopenharmony_ci			  tod_read_cmd, &val, sizeof(val));
49562306a36Sopenharmony_ci	if (err)
49662306a36Sopenharmony_ci		return err;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return _idtcm_gettime(channel, ts, 10);
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int _sync_pll_output(struct idtcm *idtcm,
50262306a36Sopenharmony_ci			    u8 pll,
50362306a36Sopenharmony_ci			    u8 sync_src,
50462306a36Sopenharmony_ci			    u8 qn,
50562306a36Sopenharmony_ci			    u8 qn_plus_1)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	int err;
50862306a36Sopenharmony_ci	u8 val;
50962306a36Sopenharmony_ci	u16 sync_ctrl0;
51062306a36Sopenharmony_ci	u16 sync_ctrl1;
51162306a36Sopenharmony_ci	u8 temp;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (qn == 0 && qn_plus_1 == 0)
51462306a36Sopenharmony_ci		return 0;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	switch (pll) {
51762306a36Sopenharmony_ci	case 0:
51862306a36Sopenharmony_ci		sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0;
51962306a36Sopenharmony_ci		sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1;
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case 1:
52262306a36Sopenharmony_ci		sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0;
52362306a36Sopenharmony_ci		sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1;
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	case 2:
52662306a36Sopenharmony_ci		sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0;
52762306a36Sopenharmony_ci		sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1;
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case 3:
53062306a36Sopenharmony_ci		sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0;
53162306a36Sopenharmony_ci		sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1;
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case 4:
53462306a36Sopenharmony_ci		sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0;
53562306a36Sopenharmony_ci		sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1;
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	case 5:
53862306a36Sopenharmony_ci		sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0;
53962306a36Sopenharmony_ci		sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1;
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci	case 6:
54262306a36Sopenharmony_ci		sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0;
54362306a36Sopenharmony_ci		sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1;
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	case 7:
54662306a36Sopenharmony_ci		sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0;
54762306a36Sopenharmony_ci		sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1;
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	default:
55062306a36Sopenharmony_ci		return -EINVAL;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	val = SYNCTRL1_MASTER_SYNC_RST;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Place master sync in reset */
55662306a36Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
55762306a36Sopenharmony_ci	if (err)
55862306a36Sopenharmony_ci		return err;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src));
56162306a36Sopenharmony_ci	if (err)
56262306a36Sopenharmony_ci		return err;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Set sync trigger mask */
56562306a36Sopenharmony_ci	val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (qn)
56862306a36Sopenharmony_ci		val |= SYNCTRL1_Q0_DIV_SYNC_TRIG;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (qn_plus_1)
57162306a36Sopenharmony_ci		val |= SYNCTRL1_Q1_DIV_SYNC_TRIG;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
57462306a36Sopenharmony_ci	if (err)
57562306a36Sopenharmony_ci		return err;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* PLL5 can have OUT8 as second additional output. */
57862306a36Sopenharmony_ci	if (pll == 5 && qn_plus_1 != 0) {
57962306a36Sopenharmony_ci		err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
58062306a36Sopenharmony_ci				 &temp, sizeof(temp));
58162306a36Sopenharmony_ci		if (err)
58262306a36Sopenharmony_ci			return err;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		temp &= ~(Q9_TO_Q8_SYNC_TRIG);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
58762306a36Sopenharmony_ci				  &temp, sizeof(temp));
58862306a36Sopenharmony_ci		if (err)
58962306a36Sopenharmony_ci			return err;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		temp |= Q9_TO_Q8_SYNC_TRIG;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE,
59462306a36Sopenharmony_ci				  &temp, sizeof(temp));
59562306a36Sopenharmony_ci		if (err)
59662306a36Sopenharmony_ci			return err;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/* PLL6 can have OUT11 as second additional output. */
60062306a36Sopenharmony_ci	if (pll == 6 && qn_plus_1 != 0) {
60162306a36Sopenharmony_ci		err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
60262306a36Sopenharmony_ci				 &temp, sizeof(temp));
60362306a36Sopenharmony_ci		if (err)
60462306a36Sopenharmony_ci			return err;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		temp &= ~(Q10_TO_Q11_SYNC_TRIG);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
60962306a36Sopenharmony_ci				  &temp, sizeof(temp));
61062306a36Sopenharmony_ci		if (err)
61162306a36Sopenharmony_ci			return err;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		temp |= Q10_TO_Q11_SYNC_TRIG;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE,
61662306a36Sopenharmony_ci				  &temp, sizeof(temp));
61762306a36Sopenharmony_ci		if (err)
61862306a36Sopenharmony_ci			return err;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* Place master sync out of reset */
62262306a36Sopenharmony_ci	val &= ~(SYNCTRL1_MASTER_SYNC_RST);
62362306a36Sopenharmony_ci	err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val));
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return err;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int idtcm_sync_pps_output(struct idtcm_channel *channel)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
63162306a36Sopenharmony_ci	u8 pll;
63262306a36Sopenharmony_ci	u8 qn;
63362306a36Sopenharmony_ci	u8 qn_plus_1;
63462306a36Sopenharmony_ci	int err = 0;
63562306a36Sopenharmony_ci	u8 out8_mux = 0;
63662306a36Sopenharmony_ci	u8 out11_mux = 0;
63762306a36Sopenharmony_ci	u8 temp;
63862306a36Sopenharmony_ci	u16 output_mask = channel->output_mask;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE,
64162306a36Sopenharmony_ci			 &temp, sizeof(temp));
64262306a36Sopenharmony_ci	if (err)
64362306a36Sopenharmony_ci		return err;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
64662306a36Sopenharmony_ci	    Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
64762306a36Sopenharmony_ci		out8_mux = 1;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE,
65062306a36Sopenharmony_ci			 &temp, sizeof(temp));
65162306a36Sopenharmony_ci	if (err)
65262306a36Sopenharmony_ci		return err;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) ==
65562306a36Sopenharmony_ci	    Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK)
65662306a36Sopenharmony_ci		out11_mux = 1;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	for (pll = 0; pll < 8; pll++) {
65962306a36Sopenharmony_ci		qn = 0;
66062306a36Sopenharmony_ci		qn_plus_1 = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		if (pll < 4) {
66362306a36Sopenharmony_ci			/* First 4 pll has 2 outputs */
66462306a36Sopenharmony_ci			qn = output_mask & 0x1;
66562306a36Sopenharmony_ci			output_mask = output_mask >> 1;
66662306a36Sopenharmony_ci			qn_plus_1 = output_mask & 0x1;
66762306a36Sopenharmony_ci			output_mask = output_mask >> 1;
66862306a36Sopenharmony_ci		} else if (pll == 4) {
66962306a36Sopenharmony_ci			if (out8_mux == 0) {
67062306a36Sopenharmony_ci				qn = output_mask & 0x1;
67162306a36Sopenharmony_ci				output_mask = output_mask >> 1;
67262306a36Sopenharmony_ci			}
67362306a36Sopenharmony_ci		} else if (pll == 5) {
67462306a36Sopenharmony_ci			if (out8_mux) {
67562306a36Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
67662306a36Sopenharmony_ci				output_mask = output_mask >> 1;
67762306a36Sopenharmony_ci			}
67862306a36Sopenharmony_ci			qn = output_mask & 0x1;
67962306a36Sopenharmony_ci			output_mask = output_mask >> 1;
68062306a36Sopenharmony_ci		} else if (pll == 6) {
68162306a36Sopenharmony_ci			qn = output_mask & 0x1;
68262306a36Sopenharmony_ci			output_mask = output_mask >> 1;
68362306a36Sopenharmony_ci			if (out11_mux) {
68462306a36Sopenharmony_ci				qn_plus_1 = output_mask & 0x1;
68562306a36Sopenharmony_ci				output_mask = output_mask >> 1;
68662306a36Sopenharmony_ci			}
68762306a36Sopenharmony_ci		} else if (pll == 7) {
68862306a36Sopenharmony_ci			if (out11_mux == 0) {
68962306a36Sopenharmony_ci				qn = output_mask & 0x1;
69062306a36Sopenharmony_ci				output_mask = output_mask >> 1;
69162306a36Sopenharmony_ci			}
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		if (qn != 0 || qn_plus_1 != 0)
69562306a36Sopenharmony_ci			err = _sync_pll_output(idtcm, pll, channel->sync_src,
69662306a36Sopenharmony_ci					       qn, qn_plus_1);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		if (err)
69962306a36Sopenharmony_ci			return err;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return err;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
70662306a36Sopenharmony_ci				  struct timespec64 const *ts,
70762306a36Sopenharmony_ci				  enum hw_tod_write_trig_sel wr_trig)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
71062306a36Sopenharmony_ci	u8 buf[TOD_BYTE_COUNT];
71162306a36Sopenharmony_ci	u8 cmd;
71262306a36Sopenharmony_ci	int err;
71362306a36Sopenharmony_ci	struct timespec64 local_ts = *ts;
71462306a36Sopenharmony_ci	s64 total_overhead_ns;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* Configure HW TOD write trigger. */
71762306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
71862306a36Sopenharmony_ci			 &cmd, sizeof(cmd));
71962306a36Sopenharmony_ci	if (err)
72062306a36Sopenharmony_ci		return err;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	cmd &= ~(0x0f);
72362306a36Sopenharmony_ci	cmd |= wr_trig | 0x08;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
72662306a36Sopenharmony_ci			  &cmd, sizeof(cmd));
72762306a36Sopenharmony_ci	if (err)
72862306a36Sopenharmony_ci		return err;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (wr_trig  != HW_TOD_WR_TRIG_SEL_MSB) {
73162306a36Sopenharmony_ci		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
73262306a36Sopenharmony_ci		if (err)
73362306a36Sopenharmony_ci			return err;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
73662306a36Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
73762306a36Sopenharmony_ci		if (err)
73862306a36Sopenharmony_ci			return err;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* ARM HW TOD write trigger. */
74262306a36Sopenharmony_ci	cmd &= ~(0x08);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1,
74562306a36Sopenharmony_ci			  &cmd, sizeof(cmd));
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) {
74862306a36Sopenharmony_ci		if (idtcm->calculate_overhead_flag) {
74962306a36Sopenharmony_ci			/* Assumption: I2C @ 400KHz */
75062306a36Sopenharmony_ci			ktime_t diff = ktime_sub(ktime_get_raw(),
75162306a36Sopenharmony_ci						 idtcm->start_time);
75262306a36Sopenharmony_ci			total_overhead_ns =  ktime_to_ns(diff)
75362306a36Sopenharmony_ci					     + idtcm->tod_write_overhead_ns
75462306a36Sopenharmony_ci					     + SETTIME_CORRECTION;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci			timespec64_add_ns(&local_ts, total_overhead_ns);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci			idtcm->calculate_overhead_flag = 0;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
76262306a36Sopenharmony_ci		if (err)
76362306a36Sopenharmony_ci			return err;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
76662306a36Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return err;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
77362306a36Sopenharmony_ci				    struct timespec64 const *ts,
77462306a36Sopenharmony_ci				    enum scsr_tod_write_trig_sel wr_trig,
77562306a36Sopenharmony_ci				    enum scsr_tod_write_type_sel wr_type)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
77862306a36Sopenharmony_ci	unsigned char buf[TOD_BYTE_COUNT], cmd;
77962306a36Sopenharmony_ci	struct timespec64 local_ts = *ts;
78062306a36Sopenharmony_ci	int err, count = 0;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	timespec64_add_ns(&local_ts, SETTIME_CORRECTION);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	err = timespec_to_char_array(&local_ts, buf, sizeof(buf));
78562306a36Sopenharmony_ci	if (err)
78662306a36Sopenharmony_ci		return err;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE,
78962306a36Sopenharmony_ci			  buf, sizeof(buf));
79062306a36Sopenharmony_ci	if (err)
79162306a36Sopenharmony_ci		return err;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/* Trigger the write operation. */
79462306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
79562306a36Sopenharmony_ci			 &cmd, sizeof(cmd));
79662306a36Sopenharmony_ci	if (err)
79762306a36Sopenharmony_ci		return err;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT);
80062306a36Sopenharmony_ci	cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT);
80162306a36Sopenharmony_ci	cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT);
80262306a36Sopenharmony_ci	cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD,
80562306a36Sopenharmony_ci			   &cmd, sizeof(cmd));
80662306a36Sopenharmony_ci	if (err)
80762306a36Sopenharmony_ci		return err;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Wait for the operation to complete. */
81062306a36Sopenharmony_ci	while (1) {
81162306a36Sopenharmony_ci		/* pps trigger takes up to 1 sec to complete */
81262306a36Sopenharmony_ci		if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS)
81362306a36Sopenharmony_ci			msleep(50);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD,
81662306a36Sopenharmony_ci				 &cmd, sizeof(cmd));
81762306a36Sopenharmony_ci		if (err)
81862306a36Sopenharmony_ci			return err;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
82162306a36Sopenharmony_ci			break;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci		if (++count > 20) {
82462306a36Sopenharmony_ci			dev_err(idtcm->dev,
82562306a36Sopenharmony_ci				"Timed out waiting for the write counter");
82662306a36Sopenharmony_ci			return -EIO;
82762306a36Sopenharmony_ci		}
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	return 0;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic int get_output_base_addr(enum fw_version ver, u8 outn)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	int base;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	switch (outn) {
83862306a36Sopenharmony_ci	case 0:
83962306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_0);
84062306a36Sopenharmony_ci		break;
84162306a36Sopenharmony_ci	case 1:
84262306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_1);
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	case 2:
84562306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_2);
84662306a36Sopenharmony_ci		break;
84762306a36Sopenharmony_ci	case 3:
84862306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_3);
84962306a36Sopenharmony_ci		break;
85062306a36Sopenharmony_ci	case 4:
85162306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_4);
85262306a36Sopenharmony_ci		break;
85362306a36Sopenharmony_ci	case 5:
85462306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_5);
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case 6:
85762306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_6);
85862306a36Sopenharmony_ci		break;
85962306a36Sopenharmony_ci	case 7:
86062306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_7);
86162306a36Sopenharmony_ci		break;
86262306a36Sopenharmony_ci	case 8:
86362306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_8);
86462306a36Sopenharmony_ci		break;
86562306a36Sopenharmony_ci	case 9:
86662306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_9);
86762306a36Sopenharmony_ci		break;
86862306a36Sopenharmony_ci	case 10:
86962306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_10);
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci	case 11:
87262306a36Sopenharmony_ci		base = IDTCM_FW_REG(ver, V520, OUTPUT_11);
87362306a36Sopenharmony_ci		break;
87462306a36Sopenharmony_ci	default:
87562306a36Sopenharmony_ci		base = -EINVAL;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	return base;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int _idtcm_settime_deprecated(struct idtcm_channel *channel,
88262306a36Sopenharmony_ci				     struct timespec64 const *ts)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
88562306a36Sopenharmony_ci	int err;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
88862306a36Sopenharmony_ci	if (err) {
88962306a36Sopenharmony_ci		dev_err(idtcm->dev,
89062306a36Sopenharmony_ci			"%s: Set HW ToD failed", __func__);
89162306a36Sopenharmony_ci		return err;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return idtcm_sync_pps_output(channel);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int _idtcm_settime(struct idtcm_channel *channel,
89862306a36Sopenharmony_ci			  struct timespec64 const *ts,
89962306a36Sopenharmony_ci			  enum scsr_tod_write_type_sel wr_type)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	return _idtcm_set_dpll_scsr_tod(channel, ts,
90262306a36Sopenharmony_ci					SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
90362306a36Sopenharmony_ci					wr_type);
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel,
90762306a36Sopenharmony_ci					  s32 offset_ns)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	int err;
91062306a36Sopenharmony_ci	int i;
91162306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
91262306a36Sopenharmony_ci	u8 buf[4];
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
91562306a36Sopenharmony_ci		buf[i] = 0xff & (offset_ns);
91662306a36Sopenharmony_ci		offset_ns >>= 8;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET,
92062306a36Sopenharmony_ci			  buf, sizeof(buf));
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return err;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel,
92662306a36Sopenharmony_ci					       u32 max_ffo_ppb)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	int err;
92962306a36Sopenharmony_ci	u8 i;
93062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
93162306a36Sopenharmony_ci	u8 buf[3];
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (max_ffo_ppb & 0xff000000)
93462306a36Sopenharmony_ci		max_ffo_ppb = 0;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
93762306a36Sopenharmony_ci		buf[i] = 0xff & (max_ffo_ppb);
93862306a36Sopenharmony_ci		max_ffo_ppb >>= 8;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
94262306a36Sopenharmony_ci			  PULL_IN_SLOPE_LIMIT, buf, sizeof(buf));
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	return err;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int idtcm_start_phase_pull_in(struct idtcm_channel *channel)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	int err;
95062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
95162306a36Sopenharmony_ci	u8 buf;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL,
95462306a36Sopenharmony_ci			 &buf, sizeof(buf));
95562306a36Sopenharmony_ci	if (err)
95662306a36Sopenharmony_ci		return err;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (buf == 0) {
95962306a36Sopenharmony_ci		buf = 0x01;
96062306a36Sopenharmony_ci		err = idtcm_write(idtcm, channel->dpll_phase_pull_in,
96162306a36Sopenharmony_ci				  PULL_IN_CTRL, &buf, sizeof(buf));
96262306a36Sopenharmony_ci	} else {
96362306a36Sopenharmony_ci		err = -EBUSY;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return err;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int do_phase_pull_in_fw(struct idtcm_channel *channel,
97062306a36Sopenharmony_ci			       s32 offset_ns,
97162306a36Sopenharmony_ci			       u32 max_ffo_ppb)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	int err;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	err = idtcm_set_phase_pull_in_offset(channel, -offset_ns);
97662306a36Sopenharmony_ci	if (err)
97762306a36Sopenharmony_ci		return err;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb);
98062306a36Sopenharmony_ci	if (err)
98162306a36Sopenharmony_ci		return err;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	err = idtcm_start_phase_pull_in(channel);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return err;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int set_tod_write_overhead(struct idtcm_channel *channel)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
99162306a36Sopenharmony_ci	s64 current_ns = 0;
99262306a36Sopenharmony_ci	s64 lowest_ns = 0;
99362306a36Sopenharmony_ci	int err;
99462306a36Sopenharmony_ci	u8 i;
99562306a36Sopenharmony_ci	ktime_t start;
99662306a36Sopenharmony_ci	ktime_t stop;
99762306a36Sopenharmony_ci	ktime_t diff;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	char buf[TOD_BYTE_COUNT] = {0};
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/* Set page offset */
100262306a36Sopenharmony_ci	idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0,
100362306a36Sopenharmony_ci		    buf, sizeof(buf));
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) {
100662306a36Sopenharmony_ci		start = ktime_get_raw();
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		err = idtcm_write(idtcm, channel->hw_dpll_n,
100962306a36Sopenharmony_ci				  HW_DPLL_TOD_OVR__0, buf, sizeof(buf));
101062306a36Sopenharmony_ci		if (err)
101162306a36Sopenharmony_ci			return err;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		stop = ktime_get_raw();
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		diff = ktime_sub(stop, start);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		current_ns = ktime_to_ns(diff);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		if (i == 0) {
102062306a36Sopenharmony_ci			lowest_ns = current_ns;
102162306a36Sopenharmony_ci		} else {
102262306a36Sopenharmony_ci			if (current_ns < lowest_ns)
102362306a36Sopenharmony_ci				lowest_ns = current_ns;
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	idtcm->tod_write_overhead_ns = lowest_ns;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return err;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	int err;
103562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
103662306a36Sopenharmony_ci	struct timespec64 ts;
103762306a36Sopenharmony_ci	s64 now;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
104062306a36Sopenharmony_ci		err = channel->do_phase_pull_in(channel, delta, 0);
104162306a36Sopenharmony_ci	} else {
104262306a36Sopenharmony_ci		idtcm->calculate_overhead_flag = 1;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci		err = set_tod_write_overhead(channel);
104562306a36Sopenharmony_ci		if (err)
104662306a36Sopenharmony_ci			return err;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		err = _idtcm_gettime_immediate(channel, &ts);
104962306a36Sopenharmony_ci		if (err)
105062306a36Sopenharmony_ci			return err;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		now = timespec64_to_ns(&ts);
105362306a36Sopenharmony_ci		now += delta;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci		ts = ns_to_timespec64(now);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		err = _idtcm_settime_deprecated(channel, &ts);
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return err;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic int idtcm_state_machine_reset(struct idtcm *idtcm)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	u8 byte = SM_RESET_CMD;
106662306a36Sopenharmony_ci	u32 status = 0;
106762306a36Sopenharmony_ci	int err;
106862306a36Sopenharmony_ci	u8 i;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	clear_boot_status(idtcm);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	err = idtcm_write(idtcm, RESET_CTRL,
107362306a36Sopenharmony_ci			  IDTCM_FW_REG(idtcm->fw_ver, V520, SM_RESET),
107462306a36Sopenharmony_ci			  &byte, sizeof(byte));
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (!err) {
107762306a36Sopenharmony_ci		for (i = 0; i < 30; i++) {
107862306a36Sopenharmony_ci			msleep_interruptible(100);
107962306a36Sopenharmony_ci			read_boot_status(idtcm, &status);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci			if (status == 0xA0) {
108262306a36Sopenharmony_ci				dev_dbg(idtcm->dev,
108362306a36Sopenharmony_ci					"SM_RESET completed in %d ms", i * 100);
108462306a36Sopenharmony_ci				break;
108562306a36Sopenharmony_ci			}
108662306a36Sopenharmony_ci		}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		if (!status)
108962306a36Sopenharmony_ci			dev_err(idtcm->dev,
109062306a36Sopenharmony_ci				"Timed out waiting for CM_RESET to complete");
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return err;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8));
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	int err;
110462306a36Sopenharmony_ci	u8 buf[2] = {0};
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf));
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	*product_id = (buf[1] << 8) | buf[0];
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	return err;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic int idtcm_read_major_release(struct idtcm *idtcm, u8 *major)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	int err;
111662306a36Sopenharmony_ci	u8 buf = 0;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf));
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	*major = buf >> 1;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	return err;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_cistatic int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8));
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	return idtcm_read(idtcm,
113362306a36Sopenharmony_ci			  GENERAL_STATUS,
113462306a36Sopenharmony_ci			  HOTFIX_REL,
113562306a36Sopenharmony_ci			  hotfix,
113662306a36Sopenharmony_ci			  sizeof(u8));
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm,
114062306a36Sopenharmony_ci					     u8 *config_select)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT,
114362306a36Sopenharmony_ci			  config_select, sizeof(u8));
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	int err = 0;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	switch (addr) {
115162306a36Sopenharmony_ci	case TOD0_OUT_ALIGN_MASK_ADDR:
115262306a36Sopenharmony_ci		SET_U16_LSB(idtcm->channel[0].output_mask, val);
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci	case TOD0_OUT_ALIGN_MASK_ADDR + 1:
115562306a36Sopenharmony_ci		SET_U16_MSB(idtcm->channel[0].output_mask, val);
115662306a36Sopenharmony_ci		break;
115762306a36Sopenharmony_ci	case TOD1_OUT_ALIGN_MASK_ADDR:
115862306a36Sopenharmony_ci		SET_U16_LSB(idtcm->channel[1].output_mask, val);
115962306a36Sopenharmony_ci		break;
116062306a36Sopenharmony_ci	case TOD1_OUT_ALIGN_MASK_ADDR + 1:
116162306a36Sopenharmony_ci		SET_U16_MSB(idtcm->channel[1].output_mask, val);
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	case TOD2_OUT_ALIGN_MASK_ADDR:
116462306a36Sopenharmony_ci		SET_U16_LSB(idtcm->channel[2].output_mask, val);
116562306a36Sopenharmony_ci		break;
116662306a36Sopenharmony_ci	case TOD2_OUT_ALIGN_MASK_ADDR + 1:
116762306a36Sopenharmony_ci		SET_U16_MSB(idtcm->channel[2].output_mask, val);
116862306a36Sopenharmony_ci		break;
116962306a36Sopenharmony_ci	case TOD3_OUT_ALIGN_MASK_ADDR:
117062306a36Sopenharmony_ci		SET_U16_LSB(idtcm->channel[3].output_mask, val);
117162306a36Sopenharmony_ci		break;
117262306a36Sopenharmony_ci	case TOD3_OUT_ALIGN_MASK_ADDR + 1:
117362306a36Sopenharmony_ci		SET_U16_MSB(idtcm->channel[3].output_mask, val);
117462306a36Sopenharmony_ci		break;
117562306a36Sopenharmony_ci	default:
117662306a36Sopenharmony_ci		err = -EFAULT; /* Bad address */;
117762306a36Sopenharmony_ci		break;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return err;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	if (index >= MAX_TOD) {
118662306a36Sopenharmony_ci		dev_err(idtcm->dev, "ToD%d not supported", index);
118762306a36Sopenharmony_ci		return -EINVAL;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (pll >= MAX_PLL) {
119162306a36Sopenharmony_ci		dev_err(idtcm->dev, "Pll%d not supported", pll);
119262306a36Sopenharmony_ci		return -EINVAL;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	idtcm->channel[index].pll = pll;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	return 0;
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic int check_and_set_masks(struct idtcm *idtcm,
120162306a36Sopenharmony_ci			       u16 regaddr,
120262306a36Sopenharmony_ci			       u8 val)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	int err = 0;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	switch (regaddr) {
120762306a36Sopenharmony_ci	case TOD_MASK_ADDR:
120862306a36Sopenharmony_ci		if ((val & 0xf0) || !(val & 0x0f)) {
120962306a36Sopenharmony_ci			dev_err(idtcm->dev, "Invalid TOD mask 0x%02x", val);
121062306a36Sopenharmony_ci			err = -EINVAL;
121162306a36Sopenharmony_ci		} else {
121262306a36Sopenharmony_ci			idtcm->tod_mask = val;
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci		break;
121562306a36Sopenharmony_ci	case TOD0_PTP_PLL_ADDR:
121662306a36Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 0, val);
121762306a36Sopenharmony_ci		break;
121862306a36Sopenharmony_ci	case TOD1_PTP_PLL_ADDR:
121962306a36Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 1, val);
122062306a36Sopenharmony_ci		break;
122162306a36Sopenharmony_ci	case TOD2_PTP_PLL_ADDR:
122262306a36Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 2, val);
122362306a36Sopenharmony_ci		break;
122462306a36Sopenharmony_ci	case TOD3_PTP_PLL_ADDR:
122562306a36Sopenharmony_ci		err = set_tod_ptp_pll(idtcm, 3, val);
122662306a36Sopenharmony_ci		break;
122762306a36Sopenharmony_ci	default:
122862306a36Sopenharmony_ci		err = set_pll_output_mask(idtcm, regaddr, val);
122962306a36Sopenharmony_ci		break;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	return err;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic void display_pll_and_masks(struct idtcm *idtcm)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	u8 i;
123862306a36Sopenharmony_ci	u8 mask;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	dev_dbg(idtcm->dev, "tod_mask = 0x%02x", idtcm->tod_mask);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	for (i = 0; i < MAX_TOD; i++) {
124362306a36Sopenharmony_ci		mask = 1 << i;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		if (mask & idtcm->tod_mask)
124662306a36Sopenharmony_ci			dev_dbg(idtcm->dev,
124762306a36Sopenharmony_ci				"TOD%d pll = %d    output_mask = 0x%04x",
124862306a36Sopenharmony_ci				i, idtcm->channel[i].pll,
124962306a36Sopenharmony_ci				idtcm->channel[i].output_mask);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic int idtcm_load_firmware(struct idtcm *idtcm,
125462306a36Sopenharmony_ci			       struct device *dev)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	u16 scratch = IDTCM_FW_REG(idtcm->fw_ver, V520, SCRATCH);
125762306a36Sopenharmony_ci	char fname[128] = FW_FILENAME;
125862306a36Sopenharmony_ci	const struct firmware *fw;
125962306a36Sopenharmony_ci	struct idtcm_fwrc *rec;
126062306a36Sopenharmony_ci	u32 regaddr;
126162306a36Sopenharmony_ci	int err;
126262306a36Sopenharmony_ci	s32 len;
126362306a36Sopenharmony_ci	u8 val;
126462306a36Sopenharmony_ci	u8 loaddr;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	if (firmware) /* module parameter */
126762306a36Sopenharmony_ci		snprintf(fname, sizeof(fname), "%s", firmware);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	dev_info(idtcm->dev, "requesting firmware '%s'", fname);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	err = request_firmware(&fw, fname, dev);
127262306a36Sopenharmony_ci	if (err) {
127362306a36Sopenharmony_ci		dev_err(idtcm->dev,
127462306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
127562306a36Sopenharmony_ci		return err;
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	dev_dbg(idtcm->dev, "firmware size %zu bytes", fw->size);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	rec = (struct idtcm_fwrc *) fw->data;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (contains_full_configuration(idtcm, fw))
128362306a36Sopenharmony_ci		idtcm_state_machine_reset(idtcm);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	for (len = fw->size; len > 0; len -= sizeof(*rec)) {
128662306a36Sopenharmony_ci		if (rec->reserved) {
128762306a36Sopenharmony_ci			dev_err(idtcm->dev,
128862306a36Sopenharmony_ci				"bad firmware, reserved field non-zero");
128962306a36Sopenharmony_ci			err = -EINVAL;
129062306a36Sopenharmony_ci		} else {
129162306a36Sopenharmony_ci			regaddr = rec->hiaddr << 8;
129262306a36Sopenharmony_ci			regaddr |= rec->loaddr;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci			val = rec->value;
129562306a36Sopenharmony_ci			loaddr = rec->loaddr;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci			rec++;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci			err = check_and_set_masks(idtcm, regaddr, val);
130062306a36Sopenharmony_ci		}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		if (err != -EINVAL) {
130362306a36Sopenharmony_ci			err = 0;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci			/* Top (status registers) and bottom are read-only */
130662306a36Sopenharmony_ci			if (regaddr < GPIO_USER_CONTROL || regaddr >= scratch)
130762306a36Sopenharmony_ci				continue;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci			/* Page size 128, last 4 bytes of page skipped */
131062306a36Sopenharmony_ci			if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
131162306a36Sopenharmony_ci				continue;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci			err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
131462306a36Sopenharmony_ci		}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci		if (err)
131762306a36Sopenharmony_ci			goto out;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	display_pll_and_masks(idtcm);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ciout:
132362306a36Sopenharmony_ci	release_firmware(fw);
132462306a36Sopenharmony_ci	return err;
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic int idtcm_output_enable(struct idtcm_channel *channel,
132862306a36Sopenharmony_ci			       bool enable, unsigned int outn)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
133162306a36Sopenharmony_ci	int base;
133262306a36Sopenharmony_ci	int err;
133362306a36Sopenharmony_ci	u8 val;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	base = get_output_base_addr(idtcm->fw_ver, outn);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (!(base > 0)) {
133862306a36Sopenharmony_ci		dev_err(idtcm->dev,
133962306a36Sopenharmony_ci			"%s - Unsupported out%d", __func__, outn);
134062306a36Sopenharmony_ci		return base;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
134462306a36Sopenharmony_ci	if (err)
134562306a36Sopenharmony_ci		return err;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (enable)
134862306a36Sopenharmony_ci		val |= SQUELCH_DISABLE;
134962306a36Sopenharmony_ci	else
135062306a36Sopenharmony_ci		val &= ~SQUELCH_DISABLE;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistatic int idtcm_perout_enable(struct idtcm_channel *channel,
135662306a36Sopenharmony_ci			       struct ptp_perout_request *perout,
135762306a36Sopenharmony_ci			       bool enable)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
136062306a36Sopenharmony_ci	struct timespec64 ts = {0, 0};
136162306a36Sopenharmony_ci	int err;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	err = idtcm_output_enable(channel, enable, perout->index);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (err) {
136662306a36Sopenharmony_ci		dev_err(idtcm->dev, "Unable to set output enable");
136762306a36Sopenharmony_ci		return err;
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	/* Align output to internal 1 PPS */
137162306a36Sopenharmony_ci	return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS);
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic int idtcm_get_pll_mode(struct idtcm_channel *channel,
137562306a36Sopenharmony_ci			      enum pll_mode *mode)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
137862306a36Sopenharmony_ci	int err;
137962306a36Sopenharmony_ci	u8 dpll_mode;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_n,
138262306a36Sopenharmony_ci			 IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
138362306a36Sopenharmony_ci			 &dpll_mode, sizeof(dpll_mode));
138462306a36Sopenharmony_ci	if (err)
138562306a36Sopenharmony_ci		return err;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	*mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	return 0;
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic int idtcm_set_pll_mode(struct idtcm_channel *channel,
139362306a36Sopenharmony_ci			      enum pll_mode mode)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
139662306a36Sopenharmony_ci	int err;
139762306a36Sopenharmony_ci	u8 dpll_mode;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_n,
140062306a36Sopenharmony_ci			 IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
140162306a36Sopenharmony_ci			 &dpll_mode, sizeof(dpll_mode));
140262306a36Sopenharmony_ci	if (err)
140362306a36Sopenharmony_ci		return err;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	dpll_mode |= (mode << PLL_MODE_SHIFT);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_n,
141062306a36Sopenharmony_ci			  IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_MODE),
141162306a36Sopenharmony_ci			  &dpll_mode, sizeof(dpll_mode));
141262306a36Sopenharmony_ci	return err;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic int idtcm_get_manual_reference(struct idtcm_channel *channel,
141662306a36Sopenharmony_ci				      enum manual_reference *ref)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
141962306a36Sopenharmony_ci	u8 dpll_manu_ref_cfg;
142062306a36Sopenharmony_ci	int err;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
142362306a36Sopenharmony_ci			 DPLL_CTRL_DPLL_MANU_REF_CFG,
142462306a36Sopenharmony_ci			 &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
142562306a36Sopenharmony_ci	if (err)
142662306a36Sopenharmony_ci		return err;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	dpll_manu_ref_cfg &= (MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	*ref = dpll_manu_ref_cfg >> MANUAL_REFERENCE_SHIFT;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return 0;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic int idtcm_set_manual_reference(struct idtcm_channel *channel,
143662306a36Sopenharmony_ci				      enum manual_reference ref)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
143962306a36Sopenharmony_ci	u8 dpll_manu_ref_cfg;
144062306a36Sopenharmony_ci	int err;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
144362306a36Sopenharmony_ci			 DPLL_CTRL_DPLL_MANU_REF_CFG,
144462306a36Sopenharmony_ci			 &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
144562306a36Sopenharmony_ci	if (err)
144662306a36Sopenharmony_ci		return err;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	dpll_manu_ref_cfg &= ~(MANUAL_REFERENCE_MASK << MANUAL_REFERENCE_SHIFT);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	dpll_manu_ref_cfg |= (ref << MANUAL_REFERENCE_SHIFT);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_ctrl_n,
145362306a36Sopenharmony_ci			  DPLL_CTRL_DPLL_MANU_REF_CFG,
145462306a36Sopenharmony_ci			  &dpll_manu_ref_cfg, sizeof(dpll_manu_ref_cfg));
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	return err;
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic int configure_dpll_mode_write_frequency(struct idtcm_channel *channel)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
146262306a36Sopenharmony_ci	int err;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (err)
146762306a36Sopenharmony_ci		dev_err(idtcm->dev, "Failed to set pll mode to write frequency");
146862306a36Sopenharmony_ci	else
146962306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	return err;
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cistatic int configure_dpll_mode_write_phase(struct idtcm_channel *channel)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
147762306a36Sopenharmony_ci	int err;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (err)
148262306a36Sopenharmony_ci		dev_err(idtcm->dev, "Failed to set pll mode to write phase");
148362306a36Sopenharmony_ci	else
148462306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	return err;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int configure_manual_reference_write_frequency(struct idtcm_channel *channel)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
149262306a36Sopenharmony_ci	int err;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_FREQUENCY);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	if (err)
149762306a36Sopenharmony_ci		dev_err(idtcm->dev, "Failed to set manual reference to write frequency");
149862306a36Sopenharmony_ci	else
149962306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	return err;
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic int configure_manual_reference_write_phase(struct idtcm_channel *channel)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
150762306a36Sopenharmony_ci	int err;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	err = idtcm_set_manual_reference(channel, MANU_REF_WRITE_PHASE);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	if (err)
151262306a36Sopenharmony_ci		dev_err(idtcm->dev, "Failed to set manual reference to write phase");
151362306a36Sopenharmony_ci	else
151462306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	return err;
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic int idtcm_stop_phase_pull_in(struct idtcm_channel *channel)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	int err;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	err = _idtcm_adjfine(channel, channel->current_freq_scaled_ppm);
152462306a36Sopenharmony_ci	if (err)
152562306a36Sopenharmony_ci		return err;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	channel->phase_pull_in = false;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	return 0;
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic long idtcm_work_handler(struct ptp_clock_info *ptp)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
153562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	(void)idtcm_stop_phase_pull_in(channel);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* Return a negative value here to not reschedule */
154462306a36Sopenharmony_ci	return -1;
154562306a36Sopenharmony_ci}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_cistatic s32 phase_pull_in_scaled_ppm(s32 current_ppm, s32 phase_pull_in_ppb)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	/* ppb = scaled_ppm * 125 / 2^13 */
155062306a36Sopenharmony_ci	/* scaled_ppm = ppb * 2^13 / 125 */
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	s64 max_scaled_ppm = div_s64((s64)PHASE_PULL_IN_MAX_PPB << 13, 125);
155362306a36Sopenharmony_ci	s64 scaled_ppm = div_s64((s64)phase_pull_in_ppb << 13, 125);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	current_ppm += scaled_ppm;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	if (current_ppm > max_scaled_ppm)
155862306a36Sopenharmony_ci		current_ppm = max_scaled_ppm;
155962306a36Sopenharmony_ci	else if (current_ppm < -max_scaled_ppm)
156062306a36Sopenharmony_ci		current_ppm = -max_scaled_ppm;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return current_ppm;
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic int do_phase_pull_in_sw(struct idtcm_channel *channel,
156662306a36Sopenharmony_ci			       s32 delta_ns,
156762306a36Sopenharmony_ci			       u32 max_ffo_ppb)
156862306a36Sopenharmony_ci{
156962306a36Sopenharmony_ci	s32 current_ppm = channel->current_freq_scaled_ppm;
157062306a36Sopenharmony_ci	u32 duration_ms = MSEC_PER_SEC;
157162306a36Sopenharmony_ci	s32 delta_ppm;
157262306a36Sopenharmony_ci	s32 ppb;
157362306a36Sopenharmony_ci	int err;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	/* If the ToD correction is less than PHASE_PULL_IN_MIN_THRESHOLD_NS,
157662306a36Sopenharmony_ci	 * skip. The error introduced by the ToD adjustment procedure would
157762306a36Sopenharmony_ci	 * be bigger than the required ToD correction
157862306a36Sopenharmony_ci	 */
157962306a36Sopenharmony_ci	if (abs(delta_ns) < PHASE_PULL_IN_MIN_THRESHOLD_NS)
158062306a36Sopenharmony_ci		return 0;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (max_ffo_ppb == 0)
158362306a36Sopenharmony_ci		max_ffo_ppb = PHASE_PULL_IN_MAX_PPB;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	/* For most cases, keep phase pull-in duration 1 second */
158662306a36Sopenharmony_ci	ppb = delta_ns;
158762306a36Sopenharmony_ci	while (abs(ppb) > max_ffo_ppb) {
158862306a36Sopenharmony_ci		duration_ms *= 2;
158962306a36Sopenharmony_ci		ppb /= 2;
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	delta_ppm = phase_pull_in_scaled_ppm(current_ppm, ppb);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	err = _idtcm_adjfine(channel, delta_ppm);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	if (err)
159762306a36Sopenharmony_ci		return err;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* schedule the worker to cancel phase pull-in */
160062306a36Sopenharmony_ci	ptp_schedule_worker(channel->ptp_clock,
160162306a36Sopenharmony_ci			    msecs_to_jiffies(duration_ms) - 1);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	channel->phase_pull_in = true;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	return 0;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int initialize_operating_mode_with_manual_reference(struct idtcm_channel *channel,
160962306a36Sopenharmony_ci							   enum manual_reference ref)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
161462306a36Sopenharmony_ci	channel->configure_write_frequency = configure_manual_reference_write_frequency;
161562306a36Sopenharmony_ci	channel->configure_write_phase = configure_manual_reference_write_phase;
161662306a36Sopenharmony_ci	channel->do_phase_pull_in = do_phase_pull_in_sw;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	switch (ref) {
161962306a36Sopenharmony_ci	case MANU_REF_WRITE_PHASE:
162062306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
162162306a36Sopenharmony_ci		break;
162262306a36Sopenharmony_ci	case MANU_REF_WRITE_FREQUENCY:
162362306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
162462306a36Sopenharmony_ci		break;
162562306a36Sopenharmony_ci	default:
162662306a36Sopenharmony_ci		dev_warn(idtcm->dev,
162762306a36Sopenharmony_ci			 "Unsupported MANUAL_REFERENCE: 0x%02x", ref);
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	return 0;
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cistatic int initialize_operating_mode_with_pll_mode(struct idtcm_channel *channel,
163462306a36Sopenharmony_ci						   enum pll_mode mode)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
163762306a36Sopenharmony_ci	int err = 0;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
164062306a36Sopenharmony_ci	channel->configure_write_frequency = configure_dpll_mode_write_frequency;
164162306a36Sopenharmony_ci	channel->configure_write_phase = configure_dpll_mode_write_phase;
164262306a36Sopenharmony_ci	channel->do_phase_pull_in = do_phase_pull_in_fw;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	switch (mode) {
164562306a36Sopenharmony_ci	case  PLL_MODE_WRITE_PHASE:
164662306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_PHASE;
164762306a36Sopenharmony_ci		break;
164862306a36Sopenharmony_ci	case PLL_MODE_WRITE_FREQUENCY:
164962306a36Sopenharmony_ci		channel->mode = PTP_PLL_MODE_WRITE_FREQUENCY;
165062306a36Sopenharmony_ci		break;
165162306a36Sopenharmony_ci	default:
165262306a36Sopenharmony_ci		dev_err(idtcm->dev,
165362306a36Sopenharmony_ci			"Unsupported PLL_MODE: 0x%02x", mode);
165462306a36Sopenharmony_ci		err = -EINVAL;
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return err;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int initialize_dco_operating_mode(struct idtcm_channel *channel)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	enum manual_reference ref = MANU_REF_XO_DPLL;
166362306a36Sopenharmony_ci	enum pll_mode mode = PLL_MODE_DISABLED;
166462306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
166562306a36Sopenharmony_ci	int err;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	channel->mode = PTP_PLL_MODE_UNSUPPORTED;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	err = idtcm_get_pll_mode(channel, &mode);
167062306a36Sopenharmony_ci	if (err) {
167162306a36Sopenharmony_ci		dev_err(idtcm->dev, "Unable to read pll mode!");
167262306a36Sopenharmony_ci		return err;
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (mode == PLL_MODE_PLL) {
167662306a36Sopenharmony_ci		err = idtcm_get_manual_reference(channel, &ref);
167762306a36Sopenharmony_ci		if (err) {
167862306a36Sopenharmony_ci			dev_err(idtcm->dev, "Unable to read manual reference!");
167962306a36Sopenharmony_ci			return err;
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci		err = initialize_operating_mode_with_manual_reference(channel, ref);
168262306a36Sopenharmony_ci	} else {
168362306a36Sopenharmony_ci		err = initialize_operating_mode_with_pll_mode(channel, mode);
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (channel->mode == PTP_PLL_MODE_WRITE_PHASE)
168762306a36Sopenharmony_ci		channel->configure_write_frequency(channel);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	return err;
169062306a36Sopenharmony_ci}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci/* PTP Hardware Clock interface */
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci/*
169562306a36Sopenharmony_ci * Maximum absolute value for write phase offset in nanoseconds
169662306a36Sopenharmony_ci *
169762306a36Sopenharmony_ci * Destination signed register is 32-bit register in resolution of 50ps
169862306a36Sopenharmony_ci *
169962306a36Sopenharmony_ci * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350 ps
170062306a36Sopenharmony_ci * Represent 107374182350 ps as 107374182 ns
170162306a36Sopenharmony_ci */
170262306a36Sopenharmony_cistatic s32 idtcm_getmaxphase(struct ptp_clock_info *ptp __always_unused)
170362306a36Sopenharmony_ci{
170462306a36Sopenharmony_ci	return MAX_ABS_WRITE_PHASE_NANOSECONDS;
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci/*
170862306a36Sopenharmony_ci * Internal function for implementing support for write phase offset
170962306a36Sopenharmony_ci *
171062306a36Sopenharmony_ci * @channel:  channel
171162306a36Sopenharmony_ci * @delta_ns: delta in nanoseconds
171262306a36Sopenharmony_ci */
171362306a36Sopenharmony_cistatic int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
171662306a36Sopenharmony_ci	int err;
171762306a36Sopenharmony_ci	u8 i;
171862306a36Sopenharmony_ci	u8 buf[4] = {0};
171962306a36Sopenharmony_ci	s32 phase_50ps;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (channel->mode != PTP_PLL_MODE_WRITE_PHASE) {
172262306a36Sopenharmony_ci		err = channel->configure_write_phase(channel);
172362306a36Sopenharmony_ci		if (err)
172462306a36Sopenharmony_ci			return err;
172562306a36Sopenharmony_ci	}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	phase_50ps = div_s64((s64)delta_ns * 1000, 50);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
173062306a36Sopenharmony_ci		buf[i] = phase_50ps & 0xff;
173162306a36Sopenharmony_ci		phase_50ps >>= 8;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
173562306a36Sopenharmony_ci			  buf, sizeof(buf));
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	return err;
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_cistatic int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
174362306a36Sopenharmony_ci	u8 i;
174462306a36Sopenharmony_ci	int err;
174562306a36Sopenharmony_ci	u8 buf[6] = {0};
174662306a36Sopenharmony_ci	s64 fcw;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (channel->mode  != PTP_PLL_MODE_WRITE_FREQUENCY) {
174962306a36Sopenharmony_ci		err = channel->configure_write_frequency(channel);
175062306a36Sopenharmony_ci		if (err)
175162306a36Sopenharmony_ci			return err;
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/*
175562306a36Sopenharmony_ci	 * Frequency Control Word unit is: 1.11 * 10^-10 ppm
175662306a36Sopenharmony_ci	 *
175762306a36Sopenharmony_ci	 * adjfreq:
175862306a36Sopenharmony_ci	 *       ppb * 10^9
175962306a36Sopenharmony_ci	 * FCW = ----------
176062306a36Sopenharmony_ci	 *          111
176162306a36Sopenharmony_ci	 *
176262306a36Sopenharmony_ci	 * adjfine:
176362306a36Sopenharmony_ci	 *       ppm_16 * 5^12
176462306a36Sopenharmony_ci	 * FCW = -------------
176562306a36Sopenharmony_ci	 *         111 * 2^4
176662306a36Sopenharmony_ci	 */
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
176962306a36Sopenharmony_ci	fcw = scaled_ppm * 244140625ULL;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	fcw = div_s64(fcw, 1776);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
177462306a36Sopenharmony_ci		buf[i] = fcw & 0xff;
177562306a36Sopenharmony_ci		fcw >>= 8;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ,
177962306a36Sopenharmony_ci			  buf, sizeof(buf));
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	return err;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
178762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
178862306a36Sopenharmony_ci	int err;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
179162306a36Sopenharmony_ci	err = _idtcm_gettime_immediate(channel, ts);
179262306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	if (err)
179562306a36Sopenharmony_ci		dev_err(idtcm->dev, "Failed at line %d in %s!",
179662306a36Sopenharmony_ci			__LINE__, __func__);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	return err;
179962306a36Sopenharmony_ci}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_cistatic int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
180262306a36Sopenharmony_ci				    const struct timespec64 *ts)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
180562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
180662306a36Sopenharmony_ci	int err;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
180962306a36Sopenharmony_ci	err = _idtcm_settime_deprecated(channel, ts);
181062306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	if (err)
181362306a36Sopenharmony_ci		dev_err(idtcm->dev,
181462306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	return err;
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_cistatic int idtcm_settime(struct ptp_clock_info *ptp,
182062306a36Sopenharmony_ci			 const struct timespec64 *ts)
182162306a36Sopenharmony_ci{
182262306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
182362306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
182462306a36Sopenharmony_ci	int err;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
182762306a36Sopenharmony_ci	err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
182862306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (err)
183162306a36Sopenharmony_ci		dev_err(idtcm->dev,
183262306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	return err;
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_cistatic int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
184062306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
184162306a36Sopenharmony_ci	int err;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
184462306a36Sopenharmony_ci	err = _idtcm_adjtime_deprecated(channel, delta);
184562306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	if (err)
184862306a36Sopenharmony_ci		dev_err(idtcm->dev,
184962306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	return err;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
185762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
185862306a36Sopenharmony_ci	struct timespec64 ts;
185962306a36Sopenharmony_ci	enum scsr_tod_write_type_sel type;
186062306a36Sopenharmony_ci	int err;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (channel->phase_pull_in == true)
186362306a36Sopenharmony_ci		return -EBUSY;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
186862306a36Sopenharmony_ci		err = channel->do_phase_pull_in(channel, delta, 0);
186962306a36Sopenharmony_ci	} else {
187062306a36Sopenharmony_ci		if (delta >= 0) {
187162306a36Sopenharmony_ci			ts = ns_to_timespec64(delta);
187262306a36Sopenharmony_ci			type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS;
187362306a36Sopenharmony_ci		} else {
187462306a36Sopenharmony_ci			ts = ns_to_timespec64(-delta);
187562306a36Sopenharmony_ci			type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS;
187662306a36Sopenharmony_ci		}
187762306a36Sopenharmony_ci		err = _idtcm_settime(channel, &ts, type);
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	if (err)
188362306a36Sopenharmony_ci		dev_err(idtcm->dev,
188462306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	return err;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
189062306a36Sopenharmony_ci{
189162306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
189262306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
189362306a36Sopenharmony_ci	int err;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
189662306a36Sopenharmony_ci	err = _idtcm_adjphase(channel, delta);
189762306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	if (err)
190062306a36Sopenharmony_ci		dev_err(idtcm->dev,
190162306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	return err;
190462306a36Sopenharmony_ci}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_cistatic int idtcm_adjfine(struct ptp_clock_info *ptp,  long scaled_ppm)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
190962306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
191062306a36Sopenharmony_ci	int err;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (channel->phase_pull_in == true)
191362306a36Sopenharmony_ci		return 0;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (scaled_ppm == channel->current_freq_scaled_ppm)
191662306a36Sopenharmony_ci		return 0;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
191962306a36Sopenharmony_ci	err = _idtcm_adjfine(channel, scaled_ppm);
192062306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	if (err)
192362306a36Sopenharmony_ci		dev_err(idtcm->dev,
192462306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
192562306a36Sopenharmony_ci	else
192662306a36Sopenharmony_ci		channel->current_freq_scaled_ppm = scaled_ppm;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	return err;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic int idtcm_enable(struct ptp_clock_info *ptp,
193262306a36Sopenharmony_ci			struct ptp_clock_request *rq, int on)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps);
193562306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
193662306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	switch (rq->type) {
194162306a36Sopenharmony_ci	case PTP_CLK_REQ_PEROUT:
194262306a36Sopenharmony_ci		if (!on)
194362306a36Sopenharmony_ci			err = idtcm_perout_enable(channel, &rq->perout, false);
194462306a36Sopenharmony_ci		/* Only accept a 1-PPS aligned to the second. */
194562306a36Sopenharmony_ci		else if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
194662306a36Sopenharmony_ci			 rq->perout.period.nsec)
194762306a36Sopenharmony_ci			err = -ERANGE;
194862306a36Sopenharmony_ci		else
194962306a36Sopenharmony_ci			err = idtcm_perout_enable(channel, &rq->perout, true);
195062306a36Sopenharmony_ci		break;
195162306a36Sopenharmony_ci	case PTP_CLK_REQ_EXTTS:
195262306a36Sopenharmony_ci		err = idtcm_extts_enable(channel, rq, on);
195362306a36Sopenharmony_ci		break;
195462306a36Sopenharmony_ci	default:
195562306a36Sopenharmony_ci		break;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	if (err)
196162306a36Sopenharmony_ci		dev_err(channel->idtcm->dev,
196262306a36Sopenharmony_ci			"Failed in %s with err %d!", __func__, err);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return err;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic int idtcm_enable_tod(struct idtcm_channel *channel)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
197062306a36Sopenharmony_ci	struct timespec64 ts = {0, 0};
197162306a36Sopenharmony_ci	u16 tod_cfg = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_CFG);
197262306a36Sopenharmony_ci	u8 cfg;
197362306a36Sopenharmony_ci	int err;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/*
197662306a36Sopenharmony_ci	 * Start the TOD clock ticking.
197762306a36Sopenharmony_ci	 */
197862306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg));
197962306a36Sopenharmony_ci	if (err)
198062306a36Sopenharmony_ci		return err;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	cfg |= TOD_ENABLE;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	err = idtcm_write(idtcm, channel->tod_n, tod_cfg, &cfg, sizeof(cfg));
198562306a36Sopenharmony_ci	if (err)
198662306a36Sopenharmony_ci		return err;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (idtcm->fw_ver < V487)
198962306a36Sopenharmony_ci		return _idtcm_settime_deprecated(channel, &ts);
199062306a36Sopenharmony_ci	else
199162306a36Sopenharmony_ci		return _idtcm_settime(channel, &ts,
199262306a36Sopenharmony_ci				      SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cistatic void idtcm_set_version_info(struct idtcm *idtcm)
199662306a36Sopenharmony_ci{
199762306a36Sopenharmony_ci	u8 major;
199862306a36Sopenharmony_ci	u8 minor;
199962306a36Sopenharmony_ci	u8 hotfix;
200062306a36Sopenharmony_ci	u16 product_id;
200162306a36Sopenharmony_ci	u8 hw_rev_id;
200262306a36Sopenharmony_ci	u8 config_select;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	idtcm_read_major_release(idtcm, &major);
200562306a36Sopenharmony_ci	idtcm_read_minor_release(idtcm, &minor);
200662306a36Sopenharmony_ci	idtcm_read_hotfix_release(idtcm, &hotfix);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	idtcm_read_product_id(idtcm, &product_id);
200962306a36Sopenharmony_ci	idtcm_read_hw_rev_id(idtcm, &hw_rev_id);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	idtcm_read_otp_scsr_config_select(idtcm, &config_select);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
201462306a36Sopenharmony_ci		 major, minor, hotfix);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	idtcm->fw_ver = idtcm_fw_version(idtcm->version);
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	dev_info(idtcm->dev,
201962306a36Sopenharmony_ci		 "%d.%d.%d, Id: 0x%04x  HW Rev: %d  OTP Config Select: %d",
202062306a36Sopenharmony_ci		 major, minor, hotfix,
202162306a36Sopenharmony_ci		 product_id, hw_rev_id, config_select);
202262306a36Sopenharmony_ci}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_cistatic int idtcm_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
202562306a36Sopenharmony_ci			    enum ptp_pin_function func, unsigned int chan)
202662306a36Sopenharmony_ci{
202762306a36Sopenharmony_ci	switch (func) {
202862306a36Sopenharmony_ci	case PTP_PF_NONE:
202962306a36Sopenharmony_ci	case PTP_PF_EXTTS:
203062306a36Sopenharmony_ci		break;
203162306a36Sopenharmony_ci	case PTP_PF_PEROUT:
203262306a36Sopenharmony_ci	case PTP_PF_PHYSYNC:
203362306a36Sopenharmony_ci		return -1;
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci	return 0;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic struct ptp_pin_desc pin_config[MAX_TOD][MAX_REF_CLK];
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_cistatic const struct ptp_clock_info idtcm_caps = {
204162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
204262306a36Sopenharmony_ci	.max_adj	= 244000,
204362306a36Sopenharmony_ci	.n_per_out	= 12,
204462306a36Sopenharmony_ci	.n_ext_ts	= MAX_TOD,
204562306a36Sopenharmony_ci	.n_pins		= MAX_REF_CLK,
204662306a36Sopenharmony_ci	.adjphase	= &idtcm_adjphase,
204762306a36Sopenharmony_ci	.getmaxphase	= &idtcm_getmaxphase,
204862306a36Sopenharmony_ci	.adjfine	= &idtcm_adjfine,
204962306a36Sopenharmony_ci	.adjtime	= &idtcm_adjtime,
205062306a36Sopenharmony_ci	.gettime64	= &idtcm_gettime,
205162306a36Sopenharmony_ci	.settime64	= &idtcm_settime,
205262306a36Sopenharmony_ci	.enable		= &idtcm_enable,
205362306a36Sopenharmony_ci	.verify		= &idtcm_verify_pin,
205462306a36Sopenharmony_ci	.do_aux_work	= &idtcm_work_handler,
205562306a36Sopenharmony_ci};
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_cistatic const struct ptp_clock_info idtcm_caps_deprecated = {
205862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
205962306a36Sopenharmony_ci	.max_adj	= 244000,
206062306a36Sopenharmony_ci	.n_per_out	= 12,
206162306a36Sopenharmony_ci	.n_ext_ts	= MAX_TOD,
206262306a36Sopenharmony_ci	.n_pins		= MAX_REF_CLK,
206362306a36Sopenharmony_ci	.adjphase	= &idtcm_adjphase,
206462306a36Sopenharmony_ci	.getmaxphase    = &idtcm_getmaxphase,
206562306a36Sopenharmony_ci	.adjfine	= &idtcm_adjfine,
206662306a36Sopenharmony_ci	.adjtime	= &idtcm_adjtime_deprecated,
206762306a36Sopenharmony_ci	.gettime64	= &idtcm_gettime,
206862306a36Sopenharmony_ci	.settime64	= &idtcm_settime_deprecated,
206962306a36Sopenharmony_ci	.enable		= &idtcm_enable,
207062306a36Sopenharmony_ci	.verify		= &idtcm_verify_pin,
207162306a36Sopenharmony_ci	.do_aux_work	= &idtcm_work_handler,
207262306a36Sopenharmony_ci};
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_cistatic int configure_channel_pll(struct idtcm_channel *channel)
207562306a36Sopenharmony_ci{
207662306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
207762306a36Sopenharmony_ci	int err = 0;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	switch (channel->pll) {
208062306a36Sopenharmony_ci	case 0:
208162306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_0;
208262306a36Sopenharmony_ci		channel->dpll_n = DPLL_0;
208362306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_0;
208462306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_0;
208562306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_0;
208662306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0;
208762306a36Sopenharmony_ci		break;
208862306a36Sopenharmony_ci	case 1:
208962306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_1;
209062306a36Sopenharmony_ci		channel->dpll_n = DPLL_1;
209162306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_1;
209262306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_1;
209362306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_1;
209462306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1;
209562306a36Sopenharmony_ci		break;
209662306a36Sopenharmony_ci	case 2:
209762306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_2;
209862306a36Sopenharmony_ci		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_2);
209962306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_2;
210062306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_2;
210162306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_2;
210262306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2;
210362306a36Sopenharmony_ci		break;
210462306a36Sopenharmony_ci	case 3:
210562306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_3;
210662306a36Sopenharmony_ci		channel->dpll_n = DPLL_3;
210762306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_3;
210862306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_3;
210962306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_3;
211062306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3;
211162306a36Sopenharmony_ci		break;
211262306a36Sopenharmony_ci	case 4:
211362306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_4;
211462306a36Sopenharmony_ci		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_4);
211562306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_4;
211662306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_4;
211762306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_4;
211862306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4;
211962306a36Sopenharmony_ci		break;
212062306a36Sopenharmony_ci	case 5:
212162306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_5;
212262306a36Sopenharmony_ci		channel->dpll_n = DPLL_5;
212362306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_5;
212462306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_5;
212562306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_5;
212662306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5;
212762306a36Sopenharmony_ci		break;
212862306a36Sopenharmony_ci	case 6:
212962306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_6;
213062306a36Sopenharmony_ci		channel->dpll_n = IDTCM_FW_REG(idtcm->fw_ver, V520, DPLL_6);
213162306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_6;
213262306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_6;
213362306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_6;
213462306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6;
213562306a36Sopenharmony_ci		break;
213662306a36Sopenharmony_ci	case 7:
213762306a36Sopenharmony_ci		channel->dpll_freq = DPLL_FREQ_7;
213862306a36Sopenharmony_ci		channel->dpll_n = DPLL_7;
213962306a36Sopenharmony_ci		channel->hw_dpll_n = HW_DPLL_7;
214062306a36Sopenharmony_ci		channel->dpll_phase = DPLL_PHASE_7;
214162306a36Sopenharmony_ci		channel->dpll_ctrl_n = DPLL_CTRL_7;
214262306a36Sopenharmony_ci		channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7;
214362306a36Sopenharmony_ci		break;
214462306a36Sopenharmony_ci	default:
214562306a36Sopenharmony_ci		err = -EINVAL;
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	return err;
214962306a36Sopenharmony_ci}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci/*
215262306a36Sopenharmony_ci * Compensate for the PTP DCO input-to-output delay.
215362306a36Sopenharmony_ci * This delay is 18 FOD cycles.
215462306a36Sopenharmony_ci */
215562306a36Sopenharmony_cistatic u32 idtcm_get_dco_delay(struct idtcm_channel *channel)
215662306a36Sopenharmony_ci{
215762306a36Sopenharmony_ci	struct idtcm *idtcm = channel->idtcm;
215862306a36Sopenharmony_ci	u8 mbuf[8] = {0};
215962306a36Sopenharmony_ci	u8 nbuf[2] = {0};
216062306a36Sopenharmony_ci	u32 fodFreq;
216162306a36Sopenharmony_ci	int err;
216262306a36Sopenharmony_ci	u64 m;
216362306a36Sopenharmony_ci	u16 n;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
216662306a36Sopenharmony_ci			 DPLL_CTRL_DPLL_FOD_FREQ, mbuf, 6);
216762306a36Sopenharmony_ci	if (err)
216862306a36Sopenharmony_ci		return 0;
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	err = idtcm_read(idtcm, channel->dpll_ctrl_n,
217162306a36Sopenharmony_ci			 DPLL_CTRL_DPLL_FOD_FREQ + 6, nbuf, 2);
217262306a36Sopenharmony_ci	if (err)
217362306a36Sopenharmony_ci		return 0;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	m = get_unaligned_le64(mbuf);
217662306a36Sopenharmony_ci	n = get_unaligned_le16(nbuf);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	if (n == 0)
217962306a36Sopenharmony_ci		n = 1;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	fodFreq = (u32)div_u64(m, n);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	if (fodFreq >= 500000000)
218462306a36Sopenharmony_ci		return (u32)div_u64(18 * (u64)NSEC_PER_SEC, fodFreq);
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	return 0;
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_cistatic int configure_channel_tod(struct idtcm_channel *channel, u32 index)
219062306a36Sopenharmony_ci{
219162306a36Sopenharmony_ci	enum fw_version fw_ver = channel->idtcm->fw_ver;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	/* Set tod addresses */
219462306a36Sopenharmony_ci	switch (index) {
219562306a36Sopenharmony_ci	case 0:
219662306a36Sopenharmony_ci		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0);
219762306a36Sopenharmony_ci		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_0);
219862306a36Sopenharmony_ci		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0);
219962306a36Sopenharmony_ci		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
220062306a36Sopenharmony_ci		channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
220162306a36Sopenharmony_ci		break;
220262306a36Sopenharmony_ci	case 1:
220362306a36Sopenharmony_ci		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1);
220462306a36Sopenharmony_ci		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_1);
220562306a36Sopenharmony_ci		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1);
220662306a36Sopenharmony_ci		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
220762306a36Sopenharmony_ci		channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
220862306a36Sopenharmony_ci		break;
220962306a36Sopenharmony_ci	case 2:
221062306a36Sopenharmony_ci		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2);
221162306a36Sopenharmony_ci		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_2);
221262306a36Sopenharmony_ci		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2);
221362306a36Sopenharmony_ci		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
221462306a36Sopenharmony_ci		channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
221562306a36Sopenharmony_ci		break;
221662306a36Sopenharmony_ci	case 3:
221762306a36Sopenharmony_ci		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3);
221862306a36Sopenharmony_ci		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_3);
221962306a36Sopenharmony_ci		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3);
222062306a36Sopenharmony_ci		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
222162306a36Sopenharmony_ci		channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
222262306a36Sopenharmony_ci		break;
222362306a36Sopenharmony_ci	default:
222462306a36Sopenharmony_ci		return -EINVAL;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return 0;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_cistatic int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
223162306a36Sopenharmony_ci{
223262306a36Sopenharmony_ci	struct idtcm_channel *channel;
223362306a36Sopenharmony_ci	int err;
223462306a36Sopenharmony_ci	int i;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	if (!(index < MAX_TOD))
223762306a36Sopenharmony_ci		return -EINVAL;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	channel = &idtcm->channel[index];
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	channel->idtcm = idtcm;
224262306a36Sopenharmony_ci	channel->current_freq_scaled_ppm = 0;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	/* Set pll addresses */
224562306a36Sopenharmony_ci	err = configure_channel_pll(channel);
224662306a36Sopenharmony_ci	if (err)
224762306a36Sopenharmony_ci		return err;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	/* Set tod addresses */
225062306a36Sopenharmony_ci	err = configure_channel_tod(channel, index);
225162306a36Sopenharmony_ci	if (err)
225262306a36Sopenharmony_ci		return err;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	if (idtcm->fw_ver < V487)
225562306a36Sopenharmony_ci		channel->caps = idtcm_caps_deprecated;
225662306a36Sopenharmony_ci	else
225762306a36Sopenharmony_ci		channel->caps = idtcm_caps;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	snprintf(channel->caps.name, sizeof(channel->caps.name),
226062306a36Sopenharmony_ci		 "IDT CM TOD%u", index);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	channel->caps.pin_config = pin_config[index];
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	for (i = 0; i < channel->caps.n_pins; ++i) {
226562306a36Sopenharmony_ci		struct ptp_pin_desc *ppd = &channel->caps.pin_config[i];
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci		snprintf(ppd->name, sizeof(ppd->name), "input_ref%d", i);
226862306a36Sopenharmony_ci		ppd->index = i;
226962306a36Sopenharmony_ci		ppd->func = PTP_PF_NONE;
227062306a36Sopenharmony_ci		ppd->chan = index;
227162306a36Sopenharmony_ci	}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	err = initialize_dco_operating_mode(channel);
227462306a36Sopenharmony_ci	if (err)
227562306a36Sopenharmony_ci		return err;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	err = idtcm_enable_tod(channel);
227862306a36Sopenharmony_ci	if (err) {
227962306a36Sopenharmony_ci		dev_err(idtcm->dev,
228062306a36Sopenharmony_ci			"Failed at line %d in %s!", __LINE__, __func__);
228162306a36Sopenharmony_ci		return err;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	channel->dco_delay = idtcm_get_dco_delay(channel);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	if (IS_ERR(channel->ptp_clock)) {
228962306a36Sopenharmony_ci		err = PTR_ERR(channel->ptp_clock);
229062306a36Sopenharmony_ci		channel->ptp_clock = NULL;
229162306a36Sopenharmony_ci		return err;
229262306a36Sopenharmony_ci	}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	if (!channel->ptp_clock)
229562306a36Sopenharmony_ci		return -ENOTSUPP;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	dev_info(idtcm->dev, "PLL%d registered as ptp%d",
229862306a36Sopenharmony_ci		 index, channel->ptp_clock->index);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	return 0;
230162306a36Sopenharmony_ci}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cistatic int idtcm_enable_extts_channel(struct idtcm *idtcm, u32 index)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	struct idtcm_channel *channel;
230662306a36Sopenharmony_ci	int err;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	if (!(index < MAX_TOD))
230962306a36Sopenharmony_ci		return -EINVAL;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	channel = &idtcm->channel[index];
231262306a36Sopenharmony_ci	channel->idtcm = idtcm;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	/* Set tod addresses */
231562306a36Sopenharmony_ci	err = configure_channel_tod(channel, index);
231662306a36Sopenharmony_ci	if (err)
231762306a36Sopenharmony_ci		return err;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	channel->idtcm = idtcm;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	return 0;
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_cistatic void idtcm_extts_check(struct work_struct *work)
232562306a36Sopenharmony_ci{
232662306a36Sopenharmony_ci	struct idtcm *idtcm = container_of(work, struct idtcm, extts_work.work);
232762306a36Sopenharmony_ci	struct idtcm_channel *channel;
232862306a36Sopenharmony_ci	u8 mask;
232962306a36Sopenharmony_ci	int err;
233062306a36Sopenharmony_ci	int i;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (idtcm->extts_mask == 0)
233362306a36Sopenharmony_ci		return;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	for (i = 0; i < MAX_TOD; i++) {
233862306a36Sopenharmony_ci		mask = 1 << i;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci		if ((idtcm->extts_mask & mask) == 0)
234162306a36Sopenharmony_ci			continue;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci		err = idtcm_extts_check_channel(idtcm, i);
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci		if (err == 0) {
234662306a36Sopenharmony_ci			/* trigger clears itself, so clear the mask */
234762306a36Sopenharmony_ci			if (idtcm->extts_single_shot) {
234862306a36Sopenharmony_ci				idtcm->extts_mask &= ~mask;
234962306a36Sopenharmony_ci			} else {
235062306a36Sopenharmony_ci				/* Re-arm */
235162306a36Sopenharmony_ci				channel = &idtcm->channel[i];
235262306a36Sopenharmony_ci				arm_tod_read_trig_sel_refclk(channel, channel->refn);
235362306a36Sopenharmony_ci			}
235462306a36Sopenharmony_ci		}
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	if (idtcm->extts_mask)
235862306a36Sopenharmony_ci		schedule_delayed_work(&idtcm->extts_work,
235962306a36Sopenharmony_ci				      msecs_to_jiffies(EXTTS_PERIOD_MS));
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_cistatic void ptp_clock_unregister_all(struct idtcm *idtcm)
236562306a36Sopenharmony_ci{
236662306a36Sopenharmony_ci	u8 i;
236762306a36Sopenharmony_ci	struct idtcm_channel *channel;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	for (i = 0; i < MAX_TOD; i++) {
237062306a36Sopenharmony_ci		channel = &idtcm->channel[i];
237162306a36Sopenharmony_ci		if (channel->ptp_clock)
237262306a36Sopenharmony_ci			ptp_clock_unregister(channel->ptp_clock);
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_cistatic void set_default_masks(struct idtcm *idtcm)
237762306a36Sopenharmony_ci{
237862306a36Sopenharmony_ci	idtcm->tod_mask = DEFAULT_TOD_MASK;
237962306a36Sopenharmony_ci	idtcm->extts_mask = 0;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	idtcm->channel[0].tod = 0;
238262306a36Sopenharmony_ci	idtcm->channel[1].tod = 1;
238362306a36Sopenharmony_ci	idtcm->channel[2].tod = 2;
238462306a36Sopenharmony_ci	idtcm->channel[3].tod = 3;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
238762306a36Sopenharmony_ci	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
238862306a36Sopenharmony_ci	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
238962306a36Sopenharmony_ci	idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
239262306a36Sopenharmony_ci	idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
239362306a36Sopenharmony_ci	idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2;
239462306a36Sopenharmony_ci	idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3;
239562306a36Sopenharmony_ci}
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_cistatic int idtcm_probe(struct platform_device *pdev)
239862306a36Sopenharmony_ci{
239962306a36Sopenharmony_ci	struct rsmu_ddata *ddata = dev_get_drvdata(pdev->dev.parent);
240062306a36Sopenharmony_ci	struct idtcm *idtcm;
240162306a36Sopenharmony_ci	int err;
240262306a36Sopenharmony_ci	u8 i;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	idtcm = devm_kzalloc(&pdev->dev, sizeof(struct idtcm), GFP_KERNEL);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	if (!idtcm)
240762306a36Sopenharmony_ci		return -ENOMEM;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	idtcm->dev = &pdev->dev;
241062306a36Sopenharmony_ci	idtcm->mfd = pdev->dev.parent;
241162306a36Sopenharmony_ci	idtcm->lock = &ddata->lock;
241262306a36Sopenharmony_ci	idtcm->regmap = ddata->regmap;
241362306a36Sopenharmony_ci	idtcm->calculate_overhead_flag = 0;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&idtcm->extts_work, idtcm_extts_check);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	set_default_masks(idtcm);
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	mutex_lock(idtcm->lock);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	idtcm_set_version_info(idtcm);
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	err = idtcm_load_firmware(idtcm, &pdev->dev);
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	if (err)
242662306a36Sopenharmony_ci		dev_warn(idtcm->dev, "loading firmware failed with %d", err);
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	wait_for_chip_ready(idtcm);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	if (idtcm->tod_mask) {
243162306a36Sopenharmony_ci		for (i = 0; i < MAX_TOD; i++) {
243262306a36Sopenharmony_ci			if (idtcm->tod_mask & (1 << i))
243362306a36Sopenharmony_ci				err = idtcm_enable_channel(idtcm, i);
243462306a36Sopenharmony_ci			else
243562306a36Sopenharmony_ci				err = idtcm_enable_extts_channel(idtcm, i);
243662306a36Sopenharmony_ci			if (err) {
243762306a36Sopenharmony_ci				dev_err(idtcm->dev,
243862306a36Sopenharmony_ci					"idtcm_enable_channel %d failed!", i);
243962306a36Sopenharmony_ci				break;
244062306a36Sopenharmony_ci			}
244162306a36Sopenharmony_ci		}
244262306a36Sopenharmony_ci	} else {
244362306a36Sopenharmony_ci		dev_err(idtcm->dev,
244462306a36Sopenharmony_ci			"no PLLs flagged as PHCs, nothing to do");
244562306a36Sopenharmony_ci		err = -ENODEV;
244662306a36Sopenharmony_ci	}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	mutex_unlock(idtcm->lock);
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	if (err) {
245162306a36Sopenharmony_ci		ptp_clock_unregister_all(idtcm);
245262306a36Sopenharmony_ci		return err;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	platform_set_drvdata(pdev, idtcm);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	return 0;
245862306a36Sopenharmony_ci}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_cistatic int idtcm_remove(struct platform_device *pdev)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	struct idtcm *idtcm = platform_get_drvdata(pdev);
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	idtcm->extts_mask = 0;
246562306a36Sopenharmony_ci	ptp_clock_unregister_all(idtcm);
246662306a36Sopenharmony_ci	cancel_delayed_work_sync(&idtcm->extts_work);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	return 0;
246962306a36Sopenharmony_ci}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_cistatic struct platform_driver idtcm_driver = {
247262306a36Sopenharmony_ci	.driver = {
247362306a36Sopenharmony_ci		.name = "8a3400x-phc",
247462306a36Sopenharmony_ci	},
247562306a36Sopenharmony_ci	.probe = idtcm_probe,
247662306a36Sopenharmony_ci	.remove	= idtcm_remove,
247762306a36Sopenharmony_ci};
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_cimodule_platform_driver(idtcm_driver);
2480