},
 };
 
+#define SJA1105_DYNAMIC_CONFIG_SLEEP_US                10
+#define SJA1105_DYNAMIC_CONFIG_TIMEOUT_US      100000
+
+static int
+sja1105_dynamic_config_poll_valid(struct sja1105_private *priv,
+                                 struct sja1105_dyn_cmd *cmd,
+                                 const struct sja1105_dynamic_table_ops *ops)
+{
+       u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {};
+       int rc;
+
+       /* We don't _need_ to read the full entry, just the command area which
+        * is a fixed SJA1105_SIZE_DYN_CMD. But our cmd_packing() API expects a
+        * buffer that contains the full entry too. Additionally, our API
+        * doesn't really know how many bytes into the buffer does the command
+        * area really begin. So just read back the whole entry.
+        */
+       rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf,
+                             ops->packed_size);
+       if (rc)
+               return rc;
+
+       /* Unpack the command structure, and return it to the caller in case it
+        * needs to perform further checks on it (VALIDENT).
+        */
+       memset(cmd, 0, sizeof(*cmd));
+       ops->cmd_packing(packed_buf, cmd, UNPACK);
+
+       /* Hardware hasn't cleared VALID => still working on it */
+       return cmd->valid ? -EAGAIN : 0;
+}
+
+/* Poll the dynamic config entry's control area until the hardware has
+ * cleared the VALID bit, which means we have confirmation that it has
+ * finished processing the command.
+ */
+static int
+sja1105_dynamic_config_wait_complete(struct sja1105_private *priv,
+                                    struct sja1105_dyn_cmd *cmd,
+                                    const struct sja1105_dynamic_table_ops *ops)
+{
+       int rc;
+
+       return read_poll_timeout(sja1105_dynamic_config_poll_valid,
+                                rc, rc != -EAGAIN,
+                                SJA1105_DYNAMIC_CONFIG_SLEEP_US,
+                                SJA1105_DYNAMIC_CONFIG_TIMEOUT_US,
+                                false, priv, cmd, ops);
+}
+
 /* Provides read access to the settings through the dynamic interface
  * of the switch.
  * @blk_idx    is used as key to select from the sja1105_dynamic_table_ops.
        struct sja1105_dyn_cmd cmd = {0};
        /* SPI payload buffer */
        u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0};
-       int retries = 3;
        int rc;
 
        if (blk_idx >= BLK_IDX_MAX_DYN)
        if (rc < 0)
                return rc;
 
-       /* Loop until we have confirmation that hardware has finished
-        * processing the command and has cleared the VALID field
-        */
-       do {
-               memset(packed_buf, 0, ops->packed_size);
-
-               /* Retrieve the read operation's result */
-               rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf,
-                                     ops->packed_size);
-               if (rc < 0)
-                       return rc;
-
-               cmd = (struct sja1105_dyn_cmd) {0};
-               ops->cmd_packing(packed_buf, &cmd, UNPACK);
-
-               if (!cmd.valident && !(ops->access & OP_VALID_ANYWAY))
-                       return -ENOENT;
-               cpu_relax();
-       } while (cmd.valid && --retries);
+       rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops);
+       if (rc < 0)
+               return rc;
 
-       if (cmd.valid)
-               return -ETIMEDOUT;
+       if (!cmd.valident && !(ops->access & OP_VALID_ANYWAY))
+               return -ENOENT;
 
        /* Don't dereference possibly NULL pointer - maybe caller
         * only wanted to see whether the entry existed or not.
        if (rc < 0)
                return rc;
 
+       rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops);
+       if (rc < 0)
+               return rc;
+
        cmd = (struct sja1105_dyn_cmd) {0};
        ops->cmd_packing(packed_buf, &cmd, UNPACK);
        if (cmd.errors)