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