ptp: clockmatrix: reset device and check BOOT_STATUS
authorMin Li <min.li.xe@renesas.com>
Tue, 8 Dec 2020 15:41:54 +0000 (10:41 -0500)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Dec 2020 23:00:55 +0000 (15:00 -0800)
SM_RESET device only when loading full configuration and check
for BOOT_STATUS. Also remove polling for write trigger done in
_idtcm_settime().

Changes since v1:
-Correct warnings from strict checkpatch

Signed-off-by: Min Li <min.li.xe@renesas.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Link: https://lore.kernel.org/r/1607442117-13661-1-git-send-email-min.li.xe@renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/ptp/idt8a340_reg.h
drivers/ptp/ptp_clockmatrix.c
drivers/ptp/ptp_clockmatrix.h

index b297c4a..a664dfe 100644 (file)
 #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
index 6632557..0ccda22 100644 (file)
@@ -33,6 +33,43 @@ 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 =
@@ -261,6 +298,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)
 {
@@ -670,7 +754,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) {
@@ -684,39 +768,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;
        }
 
@@ -891,7 +952,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;
@@ -899,13 +960,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;
 }
@@ -1099,7 +1178,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)) {
@@ -1379,7 +1458,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,
@@ -1810,7 +1889,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)
@@ -2102,6 +2181,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)) {
index 82840d7..713e41a 100644 (file)
 
 #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 {