2020-11-16 21:27:51

by Min Li

[permalink] [raw]
Subject: [PATCH net-next 1/5] ptp: clockmatrix: bug fix for idtcm_strverscmp

From: Min Li <[email protected]>

Feed kstrtou8 with NULL terminated string.

Signed-off-by: Min Li <[email protected]>
---
drivers/ptp/ptp_clockmatrix.c | 52 +++++++++++++++++++++++++++++++------------
1 file changed, 38 insertions(+), 14 deletions(-)

diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index e020faf..bf2be50 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -103,42 +103,66 @@ static int timespec_to_char_array(struct timespec64 const *ts,
return 0;
}

-static int idtcm_strverscmp(const char *ver1, const char *ver2)
+static int idtcm_strverscmp(const char *version1, const char *version2)
{
u8 num1;
u8 num2;
int result = 0;
+ char ver1[16];
+ char ver2[16];
+ char *cur1;
+ char *cur2;
+ char *next1;
+ char *next2;
+
+ strncpy(ver1, version1, 16);
+ strncpy(ver2, version2, 16);
+ cur1 = ver1;
+ cur2 = ver2;

/* loop through each level of the version string */
while (result == 0) {
+ next1 = strchr(cur1, '.');
+ next2 = strchr(cur2, '.');
+
+ /* kstrtou8 could fail for dot */
+ if (next1) {
+ *next1 = '\0';
+ next1++;
+ }
+
+ if (next2) {
+ *next2 = '\0';
+ next2++;
+ }
+
/* extract leading version numbers */
- if (kstrtou8(ver1, 10, &num1) < 0)
+ if (kstrtou8(cur1, 10, &num1) < 0)
return -1;

- if (kstrtou8(ver2, 10, &num2) < 0)
+ if (kstrtou8(cur2, 10, &num2) < 0)
return -1;

/* if numbers differ, then set the result */
- if (num1 < num2)
+ if (num1 < num2) {
result = -1;
- else if (num1 > num2)
+ } else if (num1 > num2) {
result = 1;
- else {
+ } else {
/* if numbers are the same, go to next level */
- ver1 = strchr(ver1, '.');
- ver2 = strchr(ver2, '.');
- if (!ver1 && !ver2)
+ if (!next1 && !next2)
break;
- else if (!ver1)
+ else if (!next1) {
result = -1;
- else if (!ver2)
+ } else if (!next2) {
result = 1;
- else {
- ver1++;
- ver2++;
+ } else {
+ cur1 = next1;
+ cur2 = next2;
}
}
}
+
return result;
}

--
2.7.4


2020-11-16 21:52:43

by Min Li

[permalink] [raw]
Subject: [PATCH net-next 2/5] ptp: clockmatrix: reset device and check BOOT_STATUS

From: Min Li <[email protected]>

SM_RESET device only when loading full configuration and check
for BOOT_STATUS. Also remove polling for write trigger done in
_idtcm_settime().

Signed-off-by: Min Li <[email protected]>
---
drivers/ptp/idt8a340_reg.h | 1 +
drivers/ptp/ptp_clockmatrix.c | 152 ++++++++++++++++++++++++++++++++----------
drivers/ptp/ptp_clockmatrix.h | 9 ++-
3 files changed, 126 insertions(+), 36 deletions(-)

diff --git a/drivers/ptp/idt8a340_reg.h b/drivers/ptp/idt8a340_reg.h
index b297c4a..a664dfe 100644
--- a/drivers/ptp/idt8a340_reg.h
+++ b/drivers/ptp/idt8a340_reg.h
@@ -103,6 +103,7 @@
#define SM_RESET_CMD 0x5A

#define GENERAL_STATUS 0xc014
+#define BOOT_STATUS 0x0000
#define HW_REV_ID 0x000A
#define BOND_ID 0x000B
#define HW_CSR_ID 0x000C
diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index bf2be50..5991f0f 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -33,6 +33,45 @@ module_param(firmware, charp, 0);

#define SETTIME_CORRECTION (0)

+static int contains_full_configuration(const struct firmware *fw)
+{
+ s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES;
+ struct idtcm_fwrc *rec = (struct idtcm_fwrc *) fw->data;
+ s32 count = 0;
+ u16 regaddr;
+ u8 loaddr;
+ s32 len;
+
+ /* If the firmware contains 'full configuration' SM_RESET can be used
+ * to ensure proper configuration.
+ *
+ * Full configuration is defined as the number of programmable
+ * bytes within the configuration range minus page offset addr range.
+ */
+ for (len = fw->size; len > 0; len -= sizeof(*rec)) {
+ regaddr = rec->hiaddr << 8;
+ regaddr |= rec->loaddr;
+
+ loaddr = rec->loaddr;
+
+ rec++;
+
+ /* Top (status registers) and bottom are read-only */
+ if ((regaddr < GPIO_USER_CONTROL)
+ || (regaddr >= SCRATCH))
+ continue;
+
+ /* Page size 128, last 4 bytes of page skipped */
+ if (((loaddr > 0x7b) && (loaddr <= 0x7f))
+ || loaddr > 0xfb)
+ continue;
+
+ count++;
+ }
+
+ return (count >= full_count);
+}
+
static long set_write_phase_ready(struct ptp_clock_info *ptp)
{
struct idtcm_channel *channel =
@@ -302,6 +341,53 @@ static int idtcm_write(struct idtcm *idtcm,
return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
}

+static int clear_boot_status(struct idtcm *idtcm)
+{
+ int err;
+ u8 buf[4] = {0};
+
+ err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
+
+ return err;
+}
+
+static int read_boot_status(struct idtcm *idtcm, u32 *status)
+{
+ int err;
+ u8 buf[4] = {0};
+
+ err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
+
+ *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ return err;
+}
+
+static int wait_for_boot_status_ready(struct idtcm *idtcm)
+{
+ u32 status = 0;
+ u8 i = 30; /* 30 * 100ms = 3s */
+ int err;
+
+ do {
+ err = read_boot_status(idtcm, &status);
+
+ if (err)
+ return err;
+
+ if (status == 0xA0)
+ return 0;
+
+ msleep(100);
+ i--;
+
+ } while (i);
+
+ dev_warn(&idtcm->client->dev, "%s timed out\n", __func__);
+
+ return -EBUSY;
+}
+
static int _idtcm_gettime(struct idtcm_channel *channel,
struct timespec64 *ts)
{
@@ -711,7 +797,7 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
if (err)
return err;

- if (cmd == 0)
+ if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
break;

if (++count > 20) {
@@ -725,39 +811,16 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
}

static int _idtcm_settime(struct idtcm_channel *channel,
- struct timespec64 const *ts,
- enum hw_tod_write_trig_sel wr_trig)
+ struct timespec64 const *ts)
{
struct idtcm *idtcm = channel->idtcm;
int err;
- int i;
- u8 trig_sel;
-
- err = _idtcm_set_dpll_hw_tod(channel, ts, wr_trig);
-
- if (err)
- return err;
-
- /* Wait for the operation to complete. */
- for (i = 0; i < 10000; i++) {
- err = idtcm_read(idtcm, channel->hw_dpll_n,
- HW_DPLL_TOD_CTRL_1, &trig_sel,
- sizeof(trig_sel));
-
- if (err)
- return err;

- if (trig_sel == 0x4a)
- break;
-
- err = 1;
- }
+ err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);

if (err) {
dev_err(&idtcm->client->dev,
- "Failed at line %d in func %s!\n",
- __LINE__,
- __func__);
+ "%s: Set HW ToD failed\n", __func__);
return err;
}

@@ -932,7 +995,7 @@ static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)

ts = ns_to_timespec64(now);

- err = _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
+ err = _idtcm_settime(channel, &ts);
}

return err;
@@ -940,13 +1003,31 @@ static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)

static int idtcm_state_machine_reset(struct idtcm *idtcm)
{
- int err;
u8 byte = SM_RESET_CMD;
+ u32 status = 0;
+ int err;
+ u8 i;
+
+ clear_boot_status(idtcm);

err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));

- if (!err)
- msleep_interruptible(POST_SM_RESET_DELAY_MS);
+ if (!err) {
+ for (i = 0; i < 30; i++) {
+ msleep_interruptible(100);
+ read_boot_status(idtcm, &status);
+
+ if (status == 0xA0) {
+ dev_dbg(&idtcm->client->dev,
+ "SM_RESET completed in %d ms\n",
+ i * 100);
+ break;
+ }
+ }
+
+ if (!status)
+ dev_err(&idtcm->client->dev, "Timed out waiting for CM_RESET to complete\n");
+ }

return err;
}
@@ -1140,7 +1221,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm,

rec = (struct idtcm_fwrc *) fw->data;

- if (fw->size > 0)
+ if (contains_full_configuration(fw))
idtcm_state_machine_reset(idtcm);

for (len = fw->size; len > 0; len -= sizeof(*rec)) {
@@ -1420,7 +1501,7 @@ static int idtcm_settime(struct ptp_clock_info *ptp,

mutex_lock(&idtcm->reg_lock);

- err = _idtcm_settime(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
+ err = _idtcm_settime(channel, ts);

if (err)
dev_err(&idtcm->client->dev,
@@ -1851,7 +1932,7 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
if (err)
return err;

- return _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
+ return _idtcm_settime(channel, &ts);
}

static void idtcm_display_version_info(struct idtcm *idtcm)
@@ -2143,6 +2224,9 @@ static int idtcm_probe(struct i2c_client *client,
dev_warn(&idtcm->client->dev,
"loading firmware failed with %d\n", err);

+ if (wait_for_boot_status_ready(idtcm))
+ dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0\n");
+
if (idtcm->tod_mask) {
for (i = 0; i < MAX_TOD; i++) {
if (idtcm->tod_mask & (1 << i)) {
diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 82840d7..713e41a 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -53,9 +53,14 @@

#define OUTPUT_MODULE_FROM_INDEX(index) (OUTPUT_0 + (index) * 0x10)

-#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
+#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)

-#define IDTCM_MAX_WRITE_COUNT (512)
+#define IDTCM_MAX_WRITE_COUNT (512)
+
+#define FULL_FW_CFG_BYTES (SCRATCH - GPIO_USER_CONTROL)
+#define FULL_FW_CFG_SKIPPED_BYTES (((SCRATCH >> 7) \
+ - (GPIO_USER_CONTROL >> 7)) \
+ * 4) /* 4 bytes skipped every 0x80 */

/* Values of DPLL_N.DPLL_MODE.PLL_MODE */
enum pll_mode {
--
2.7.4

2020-11-16 21:52:49

by Min Li

[permalink] [raw]
Subject: [PATCH net-next 3/5] ptp: clockmatrix: remove 5 second delay before entering write phase mode

From: Min Li <[email protected]>

Remove write phase mode 5 second setup delay, not needed.

Signed-off-by: Min Li <[email protected]>
---
drivers/ptp/ptp_clockmatrix.c | 22 ----------------------
drivers/ptp/ptp_clockmatrix.h | 1 -
2 files changed, 23 deletions(-)

diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index 5991f0f..9af6335 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -72,16 +72,6 @@ static int contains_full_configuration(const struct firmware *fw)
return (count >= full_count);
}

-static long set_write_phase_ready(struct ptp_clock_info *ptp)
-{
- struct idtcm_channel *channel =
- container_of(ptp, struct idtcm_channel, caps);
-
- channel->write_phase_ready = 1;
-
- return 0;
-}
-
static int char_array_to_timespec(u8 *buf,
u8 count,
struct timespec64 *ts)
@@ -1382,16 +1372,8 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)

if (err)
return err;
-
- channel->write_phase_ready = 0;
-
- ptp_schedule_worker(channel->ptp_clock,
- msecs_to_jiffies(WR_PHASE_SETUP_MS));
}

- if (!channel->write_phase_ready)
- delta_ns = 0;
-
offset_ps = (s64)delta_ns * 1000;

/*
@@ -1971,7 +1953,6 @@ static const struct ptp_clock_info idtcm_caps_v487 = {
.gettime64 = &idtcm_gettime,
.settime64 = &idtcm_settime_v487,
.enable = &idtcm_enable,
- .do_aux_work = &set_write_phase_ready,
};

static const struct ptp_clock_info idtcm_caps = {
@@ -1984,7 +1965,6 @@ static const struct ptp_clock_info idtcm_caps = {
.gettime64 = &idtcm_gettime,
.settime64 = &idtcm_settime,
.enable = &idtcm_enable,
- .do_aux_work = &set_write_phase_ready,
};

static int configure_channel_pll(struct idtcm_channel *channel)
@@ -2154,8 +2134,6 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
if (!channel->ptp_clock)
return -ENOTSUPP;

- channel->write_phase_ready = 0;
-
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
index, channel->ptp_clock->index);

diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 713e41a..dd3436e 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -125,7 +125,6 @@ struct idtcm_channel {
enum pll_mode pll_mode;
u8 pll;
u16 output_mask;
- int write_phase_ready;
};

struct idtcm {
--
2.7.4

2020-11-17 01:29:28

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next 1/5] ptp: clockmatrix: bug fix for idtcm_strverscmp

On Mon, 16 Nov 2020 14:27:26 -0500 [email protected] wrote:
> From: Min Li <[email protected]>
>
> Feed kstrtou8 with NULL terminated string.

Is this a fix? Does it need to be backported to stable?

Also please add a cover letter for the series describing the overall
goal of this work, what testing has been done etc.

> drivers/ptp/ptp_clockmatrix.c | 52 +++++++++++++++++++++++++++++++------------
> 1 file changed, 38 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
> index e020faf..bf2be50 100644
> --- a/drivers/ptp/ptp_clockmatrix.c
> +++ b/drivers/ptp/ptp_clockmatrix.c
> @@ -103,42 +103,66 @@ static int timespec_to_char_array(struct timespec64 const *ts,
> return 0;
> }
>
> -static int idtcm_strverscmp(const char *ver1, const char *ver2)
> +static int idtcm_strverscmp(const char *version1, const char *version2)
> {
> u8 num1;
> u8 num2;
> int result = 0;
> + char ver1[16];
> + char ver2[16];
> + char *cur1;
> + char *cur2;
> + char *next1;
> + char *next2;
> +
> + strncpy(ver1, version1, 16);
> + strncpy(ver2, version2, 16);

This patch triggers the following warning on a 32bit build:

In file included from ../arch/x86/include/asm/page_32.h:35,
from ../arch/x86/include/asm/page.h:14,
from ../arch/x86/include/asm/thread_info.h:12,
from ../include/linux/thread_info.h:38,
from ../arch/x86/include/asm/preempt.h:7,
from ../include/linux/preempt.h:78,
from ../include/linux/spinlock.h:51,
from ../include/linux/mmzone.h:8,
from ../include/linux/gfp.h:6,
from ../include/linux/firmware.h:7,
from ../drivers/ptp/ptp_clockmatrix.c:8:
In function ‘strncpy’,
inlined from ‘idtcm_strverscmp.constprop’ at ../drivers/ptp/ptp_clockmatrix.c:118:2:
../include/linux/string.h:290:30: warning: ‘__builtin_strncpy’ specified bound 16 equals destination size [-Wstringop-truncation]
290 | #define __underlying_strncpy __builtin_strncpy
| ^
../include/linux/string.h:300:9: note: in expansion of macro ‘__underlying_strncpy’
300 | return __underlying_strncpy(p, q, size);
| ^~~~~~~~~~~~~~~~~~~~

2020-11-17 02:00:33

by Min Li

[permalink] [raw]
Subject: [PATCH net-next 5/5] ptp: clockmatrix: deprecate firmware older than 4.8.7

From: Min Li <[email protected]>

Add deprecated flag to indicate < v4.8.7.
Fix idtcm_enable_tod() call correct settime().

Signed-off-by: Min Li <[email protected]>
---
drivers/ptp/ptp_clockmatrix.c | 69 ++++++++++++++++++++++++-------------------
drivers/ptp/ptp_clockmatrix.h | 11 +++----
2 files changed, 45 insertions(+), 35 deletions(-)

diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index 9b3ba92..c71a570 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -939,8 +939,8 @@ static void restore_output_phase_adj(struct idtcm_channel *channel)
}
}

-static int _idtcm_settime(struct idtcm_channel *channel,
- struct timespec64 const *ts)
+static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
+ struct timespec64 const *ts)
{
struct idtcm *idtcm = channel->idtcm;
int retval;
@@ -965,9 +965,9 @@ static int _idtcm_settime(struct idtcm_channel *channel,
return retval;
}

-static int _idtcm_settime_v487(struct idtcm_channel *channel,
- struct timespec64 const *ts,
- enum scsr_tod_write_type_sel wr_type)
+static int _idtcm_settime(struct idtcm_channel *channel,
+ struct timespec64 const *ts,
+ enum scsr_tod_write_type_sel wr_type)
{
return _idtcm_set_dpll_scsr_tod(channel, ts,
SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
@@ -1109,14 +1109,14 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)
return err;
}

-static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
+static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
{
int err;
struct idtcm *idtcm = channel->idtcm;
struct timespec64 ts;
s64 now;

- if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
+ if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
err = idtcm_do_phase_pull_in(channel, delta, 0);
} else {
idtcm->calculate_overhead_flag = 1;
@@ -1136,7 +1136,7 @@ static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)

ts = ns_to_timespec64(now);

- err = _idtcm_settime(channel, &ts);
+ err = _idtcm_settime_deprecated(channel, &ts);
}

return err;
@@ -1640,8 +1640,8 @@ static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
return err;
}

-static int idtcm_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
+static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1650,7 +1650,7 @@ static int idtcm_settime(struct ptp_clock_info *ptp,

mutex_lock(&idtcm->reg_lock);

- err = _idtcm_settime(channel, ts);
+ err = _idtcm_settime_deprecated(channel, ts);

if (err)
dev_err(&idtcm->client->dev,
@@ -1663,7 +1663,7 @@ static int idtcm_settime(struct ptp_clock_info *ptp,
return err;
}

-static int idtcm_settime_v487(struct ptp_clock_info *ptp,
+static int idtcm_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct idtcm_channel *channel =
@@ -1673,7 +1673,7 @@ static int idtcm_settime_v487(struct ptp_clock_info *ptp,

mutex_lock(&idtcm->reg_lock);

- err = _idtcm_settime_v487(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
+ err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);

if (err)
dev_err(&idtcm->client->dev,
@@ -1686,7 +1686,7 @@ static int idtcm_settime_v487(struct ptp_clock_info *ptp,
return err;
}

-static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
+static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1695,7 +1695,7 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)

mutex_lock(&idtcm->reg_lock);

- err = _idtcm_adjtime(channel, delta);
+ err = _idtcm_adjtime_deprecated(channel, delta);

if (err)
dev_err(&idtcm->client->dev,
@@ -1708,7 +1708,7 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
return err;
}

-static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
+static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1717,7 +1717,7 @@ static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
enum scsr_tod_write_type_sel type;
int err;

- if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_V487) {
+ if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
err = idtcm_do_phase_pull_in(channel, delta, 0);
if (err)
dev_err(&idtcm->client->dev,
@@ -1737,7 +1737,7 @@ static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)

mutex_lock(&idtcm->reg_lock);

- err = _idtcm_settime_v487(channel, &ts, type);
+ err = _idtcm_settime(channel, &ts, type);

if (err)
dev_err(&idtcm->client->dev,
@@ -2081,10 +2081,14 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
if (err)
return err;

- return _idtcm_settime(channel, &ts);
+ if (idtcm->deprecated)
+ return _idtcm_settime_deprecated(channel, &ts);
+ else
+ return _idtcm_settime(channel, &ts,
+ SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
}

-static void idtcm_display_version_info(struct idtcm *idtcm)
+static void idtcm_set_version_info(struct idtcm *idtcm)
{
u8 major;
u8 minor;
@@ -2106,31 +2110,36 @@ static void idtcm_display_version_info(struct idtcm *idtcm)
snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
major, minor, hotfix);

+ if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
+ idtcm->deprecated = 0;
+ else
+ idtcm->deprecated = 1;
+
dev_info(&idtcm->client->dev, fmt, major, minor, hotfix,
product_id, hw_rev_id, config_select);
}

-static const struct ptp_clock_info idtcm_caps_v487 = {
+static const struct ptp_clock_info idtcm_caps = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 12,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
- .adjtime = &idtcm_adjtime_v487,
+ .adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime,
- .settime64 = &idtcm_settime_v487,
+ .settime64 = &idtcm_settime,
.enable = &idtcm_enable,
};

-static const struct ptp_clock_info idtcm_caps = {
+static const struct ptp_clock_info idtcm_caps_deprecated = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 12,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
- .adjtime = &idtcm_adjtime,
+ .adjtime = &idtcm_adjtime_deprecated,
.gettime64 = &idtcm_gettime,
- .settime64 = &idtcm_settime,
+ .settime64 = &idtcm_settime_deprecated,
.enable = &idtcm_enable,
};

@@ -2253,15 +2262,15 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)

channel->idtcm = idtcm;

- if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
- channel->caps = idtcm_caps_v487;
+ if (idtcm->deprecated)
+ channel->caps = idtcm_caps_deprecated;
else
channel->caps = idtcm_caps;

snprintf(channel->caps.name, sizeof(channel->caps.name),
"IDT CM TOD%u", index);

- if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) {
+ if (!idtcm->deprecated) {
err = idtcm_enable_tod_sync(channel);
if (err) {
dev_err(&idtcm->client->dev,
@@ -2360,7 +2369,7 @@ static int idtcm_probe(struct i2c_client *client,
mutex_init(&idtcm->reg_lock);
mutex_lock(&idtcm->reg_lock);

- idtcm_display_version_info(idtcm);
+ idtcm_set_version_info(idtcm);

err = idtcm_load_firmware(idtcm, &client->dev);

diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 3790dfa..645de2c 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -45,11 +45,11 @@
#define DEFAULT_TOD2_PTP_PLL (2)
#define DEFAULT_TOD3_PTP_PLL (3)

-#define POST_SM_RESET_DELAY_MS (3000)
-#define PHASE_PULL_IN_THRESHOLD_NS (150000)
-#define PHASE_PULL_IN_THRESHOLD_NS_V487 (15000)
-#define TOD_WRITE_OVERHEAD_COUNT_MAX (2)
-#define TOD_BYTE_COUNT (11)
+#define POST_SM_RESET_DELAY_MS (3000)
+#define PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED (150000)
+#define PHASE_PULL_IN_THRESHOLD_NS (15000)
+#define TOD_WRITE_OVERHEAD_COUNT_MAX (2)
+#define TOD_BYTE_COUNT (11)

#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)

@@ -132,6 +132,7 @@ struct idtcm {
u8 page_offset;
u8 tod_mask;
char version[16];
+ u8 deprecated;

/* Overhead calculation for adjtime */
u8 calculate_overhead_flag;
--
2.7.4

2020-11-17 02:02:02

by Min Li

[permalink] [raw]
Subject: [PATCH net-next 4/5] ptp: clockmatrix: Fix non-zero phase_adj is lost after snap

From: Min Li <[email protected]>

Fix non-zero phase_adj is lost after snap. Use ktime_sub
to do ktime_t subtraction.

Signed-off-by: Min Li <[email protected]>
---
drivers/ptp/ptp_clockmatrix.c | 210 +++++++++++++++++++++++++++++++++++++-----
drivers/ptp/ptp_clockmatrix.h | 5 +-
2 files changed, 190 insertions(+), 25 deletions(-)

diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index 9af6335..9b3ba92 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -716,8 +716,9 @@ static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,

if (idtcm->calculate_overhead_flag) {
/* Assumption: I2C @ 400KHz */
- total_overhead_ns = ktime_to_ns(ktime_get_raw()
- - idtcm->start_time)
+ ktime_t diff = ktime_sub(ktime_get_raw(),
+ idtcm->start_time);
+ total_overhead_ns = ktime_to_ns(diff)
+ idtcm->tod_write_overhead_ns
+ SETTIME_CORRECTION;

@@ -800,12 +801,154 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
return 0;
}

+static int get_output_base_addr(u8 outn)
+{
+ int base;
+
+ switch (outn) {
+ case 0:
+ base = OUTPUT_0;
+ break;
+ case 1:
+ base = OUTPUT_1;
+ break;
+ case 2:
+ base = OUTPUT_2;
+ break;
+ case 3:
+ base = OUTPUT_3;
+ break;
+ case 4:
+ base = OUTPUT_4;
+ break;
+ case 5:
+ base = OUTPUT_5;
+ break;
+ case 6:
+ base = OUTPUT_6;
+ break;
+ case 7:
+ base = OUTPUT_7;
+ break;
+ case 8:
+ base = OUTPUT_8;
+ break;
+ case 9:
+ base = OUTPUT_9;
+ break;
+ case 10:
+ base = OUTPUT_10;
+ break;
+ case 11:
+ base = OUTPUT_11;
+ break;
+ default:
+ base = -EINVAL;
+ }
+
+ return base;
+}
+
+static void save_and_clear_output_phase_adj(struct idtcm_channel *channel)
+{
+ u16 output_mask = channel->output_mask;
+ struct idtcm *idtcm = channel->idtcm;
+ int delay_needed = 0;
+ u8 zero[4] = {0};
+ u8 outn = 0;
+ int base;
+
+ while (output_mask) {
+
+ if (output_mask & 1) {
+
+ base = get_output_base_addr(outn);
+
+ if (!(base > 0)) {
+ dev_err(&idtcm->client->dev,
+ "%s - Unsupported out%d",
+ __func__, outn);
+ return;
+ }
+
+ /* Save output_phase_adj for outn */
+ idtcm_read(idtcm, (u16)base, OUT_PHASE_ADJ,
+ &channel->output_phase_adj[outn][0],
+ sizeof(channel->output_phase_adj[outn]));
+
+ if (channel->output_phase_adj[outn][0] |
+ channel->output_phase_adj[outn][1] |
+ channel->output_phase_adj[outn][2] |
+ channel->output_phase_adj[outn][3]) {
+ delay_needed = 1;
+
+ idtcm_write(idtcm, base, OUT_PHASE_ADJ,
+ &zero[0], sizeof(zero));
+ }
+ }
+
+ output_mask = output_mask >> 1;
+ outn += 1;
+ }
+
+ /* Ensure output phase adjust has settled */
+ if (delay_needed)
+ msleep(5000);
+}
+
+
+static void restore_output_phase_adj(struct idtcm_channel *channel)
+{
+ u16 output_mask = channel->output_mask;
+ struct idtcm *idtcm = channel->idtcm;
+ u8 wait_once = 0;
+ u8 outn = 0;
+ int base;
+
+ while (output_mask) {
+
+ if ((output_mask & 1) &&
+ (channel->output_phase_adj[outn][0] |
+ channel->output_phase_adj[outn][1] |
+ channel->output_phase_adj[outn][2] |
+ channel->output_phase_adj[outn][3])) {
+
+ if (!wait_once) {
+ /* Ensure idtcm_sync_pps_output() is done */
+ msleep(5000);
+ wait_once = 1;
+ }
+
+ base = get_output_base_addr(outn);
+
+ if (!(base > 0)) {
+ dev_err(&idtcm->client->dev,
+ "%s - Unsupported out%d",
+ __func__, outn);
+ return;
+ }
+
+ /* Restore non-zero output_phase_adj */
+ idtcm_write(idtcm, base, OUT_PHASE_ADJ,
+ &channel->output_phase_adj[outn][0],
+ sizeof(channel->output_phase_adj[outn]));
+ }
+
+ output_mask = output_mask >> 1;
+ outn += 1;
+ }
+}
+
static int _idtcm_settime(struct idtcm_channel *channel,
struct timespec64 const *ts)
{
struct idtcm *idtcm = channel->idtcm;
+ int retval;
int err;

+ /* Save and clear out_phase_adj */
+ save_and_clear_output_phase_adj(channel);
+
err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);

if (err) {
@@ -814,7 +957,12 @@ static int _idtcm_settime(struct idtcm_channel *channel,
return err;
}

- return idtcm_sync_pps_output(channel);
+ retval = idtcm_sync_pps_output(channel);
+
+ /* Restore out_phase_adj */
+ restore_output_phase_adj(channel);
+
+ return retval;
}

static int _idtcm_settime_v487(struct idtcm_channel *channel,
@@ -924,6 +1072,7 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)

ktime_t start;
ktime_t stop;
+ ktime_t diff;

char buf[TOD_BYTE_COUNT] = {0};

@@ -943,7 +1092,9 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)

stop = ktime_get_raw();

- current_ns = ktime_to_ns(stop - start);
+ diff = ktime_sub(stop, start);
+
+ current_ns = ktime_to_ns(diff);

if (i == 0) {
lowest_ns = current_ns;
@@ -1263,11 +1414,19 @@ static int idtcm_output_enable(struct idtcm_channel *channel,
bool enable, unsigned int outn)
{
struct idtcm *idtcm = channel->idtcm;
+ int base;
int err;
u8 val;

- err = idtcm_read(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
- OUT_CTRL_1, &val, sizeof(val));
+ base = get_output_base_addr(outn);
+
+ if (!(base > 0)) {
+ dev_err(&idtcm->client->dev,
+ "%s - Unsupported out%d", __func__, outn);
+ return base;
+ }
+
+ err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));

if (err)
return err;
@@ -1277,8 +1436,7 @@ static int idtcm_output_enable(struct idtcm_channel *channel,
else
val &= ~SQUELCH_DISABLE;

- return idtcm_write(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
- OUT_CTRL_1, &val, sizeof(val));
+ return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
}

static int idtcm_output_mask_enable(struct idtcm_channel *channel,
@@ -1321,6 +1479,23 @@ static int idtcm_perout_enable(struct idtcm_channel *channel,
return idtcm_output_enable(channel, enable, perout->index);
}

+static int idtcm_get_pll_mode(struct idtcm_channel *channel,
+ enum pll_mode *pll_mode)
+{
+ struct idtcm *idtcm = channel->idtcm;
+ int err;
+ u8 dpll_mode;
+
+ err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
+ &dpll_mode, sizeof(dpll_mode));
+ if (err)
+ return err;
+
+ *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
+
+ return 0;
+}
+
static int idtcm_set_pll_mode(struct idtcm_channel *channel,
enum pll_mode pll_mode)
{
@@ -1386,7 +1561,7 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;

- phase_50ps = DIV_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
+ phase_50ps = div_s64(offset_ps, 50);

for (i = 0; i < 4; i++) {
buf[i] = phase_50ps & 0xff;
@@ -1403,7 +1578,6 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
{
struct idtcm *idtcm = channel->idtcm;
u8 i;
- bool neg_adj = 0;
int err;
u8 buf[6] = {0};
s64 fcw;
@@ -1427,18 +1601,11 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
* FCW = -------------
* 111 * 2^4
*/
- if (scaled_ppm < 0) {
- neg_adj = 1;
- scaled_ppm = -scaled_ppm;
- }

/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
fcw = scaled_ppm * 244140625ULL;

- fcw = div_u64(fcw, 1776);
-
- if (neg_adj)
- fcw = -fcw;
+ fcw = div_s64(fcw, 1776);

for (i = 0; i < 6; i++) {
buf[i] = fcw & 0xff;
@@ -2105,12 +2272,11 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
}
}

- err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
+ /* Sync pll mode with hardware */
+ err = idtcm_get_pll_mode(channel, &channel->pll_mode);
if (err) {
dev_err(&idtcm->client->dev,
- "Failed at line %d in func %s!\n",
- __LINE__,
- __func__);
+ "Error: %s - Unable to read pll mode\n", __func__);
return err;
}

diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index dd3436e..3790dfa 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -15,6 +15,7 @@
#define FW_FILENAME "idtcm.bin"
#define MAX_TOD (4)
#define MAX_PLL (8)
+#define MAX_OUTPUT (12)

#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)

@@ -49,9 +50,6 @@
#define PHASE_PULL_IN_THRESHOLD_NS_V487 (15000)
#define TOD_WRITE_OVERHEAD_COUNT_MAX (2)
#define TOD_BYTE_COUNT (11)
-#define WR_PHASE_SETUP_MS (5000)
-
-#define OUTPUT_MODULE_FROM_INDEX(index) (OUTPUT_0 + (index) * 0x10)

#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)

@@ -125,6 +123,7 @@ struct idtcm_channel {
enum pll_mode pll_mode;
u8 pll;
u16 output_mask;
+ u8 output_phase_adj[MAX_OUTPUT][4];
};

struct idtcm {
--
2.7.4