Skip to content

Commit 994c340

Browse files
committed
d4xx: add post-reset HWMC readiness gate and fix GVD retry logic
RSDSO-21151 After HW reset, the driver returned success to userspace ~54ms before the firmware's HWMC subsystem was ready for commands. RS Viewer immediately queried GVD/HWMC, hitting I2C EREMOTEIO (-121) failures and WIP/ERR status codes for seconds, eventually crashing. Root causes and fixes: 1) ds5_hw_reset_with_recovery: Add Step 9 HWMC readiness check. After device type is confirmed (Step 8), send a lightweight GVD command and poll HWMC_STATUS until completion before returning to userspace. This blocks the caller until the FW command processor is fully operational, preventing the race window. 2) ds5_gvd: Fix retry loop condition. The old condition 'ret && retries-- && status != 0' only retried on I2C failure. If I2C succeeded but status was WIP (2), ret==0 caused immediate loop exit. Changed to '(ret || status == WIP) && retries--' to retry on both I2C errors and WIP. Increased retries from 3x10ms to 20x50ms for transient post-reset conditions. 3) ds5_gvd: Fix return value. Previously returned raw HWMC status (positive int, e.g. 2 for WIP) on failure. V4L2 g_volatile_ctrl interprets positive returns as success, so userspace received garbage GVD data. Now returns -EIO on failure. 4) ds5_get_hwmc_status: Add I2C retry tolerance. The loop exited immediately on I2C failure (!ret was false). Changed condition to continue retrying on both I2C failures and WIP status, with a dev_dbg trace for I2C errors. Signed-off-by: GitHub Copilot <copilot@github.com>
1 parent 4254890 commit 994c340

1 file changed

Lines changed: 84 additions & 10 deletions

File tree

kernel/realsense/d4xx.c

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,7 +2177,12 @@ static int ds5_get_hwmc_status(struct ds5 *state)
21772177
if (retries != 100)
21782178
msleep_range(1);
21792179
ret = ds5_read(state, DS5_HWMC_STATUS, &status);
2180-
} while (!ret && retries-- && status == DS5_HWMC_STATUS_WIP);
2180+
if (ret) {
2181+
dev_dbg(&state->client->dev,
2182+
"%s(): I2C read failed (%d), retries left: %d\n",
2183+
__func__, ret, retries);
2184+
}
2185+
} while (retries-- && (ret || status == DS5_HWMC_STATUS_WIP));
21812186
dev_dbg(&state->client->dev,
21822187
"%s(): ret: 0x%x, status: 0x%x\n",
21832188
__func__, ret, status);
@@ -2730,6 +2735,75 @@ static int ds5_hw_reset_with_recovery(struct ds5 *state)
27302735
__func__, dev_type, DS5_HW_RESET_TIMEOUT_MS);
27312736
}
27322737

2738+
/* 9. Verify HWMC subsystem is ready to accept commands.
2739+
* After HW reset the FW reports 0xDEAD and populates device type
2740+
* before the HWM command processor is fully initialized. If
2741+
* userspace queries GVD/HWMC immediately after we return, the
2742+
* I2C reads NAK (EREMOTEIO) or return stale status (WIP/ERR),
2743+
* which can crash RS Viewer. Send a lightweight GVD command and
2744+
* poll HWMC_STATUS until it completes successfully.
2745+
*/
2746+
{
2747+
struct hwm_cmd hwmc_probe;
2748+
u16 hwmc_status = DS5_HWMC_STATUS_WIP;
2749+
int hwmc_ret;
2750+
2751+
memcpy(&hwmc_probe, &gvd, sizeof(gvd));
2752+
2753+
for (retry = 0; retry < DS5_HW_RESET_MAX_RETRIES; retry++) {
2754+
hwmc_ret = ds5_raw_write(state, DS5_HWMC_DATA,
2755+
&hwmc_probe, sizeof(hwmc_probe));
2756+
if (hwmc_ret) {
2757+
dev_dbg(&state->client->dev,
2758+
"%s(): HWMC probe write failed (%d), retry %d\n",
2759+
__func__, hwmc_ret, retry);
2760+
msleep(DS5_HW_RESET_POLL_INTERVAL_MS);
2761+
continue;
2762+
}
2763+
2764+
hwmc_ret = ds5_write(state, DS5_HWMC_EXEC, 0x01);
2765+
if (hwmc_ret) {
2766+
dev_dbg(&state->client->dev,
2767+
"%s(): HWMC probe exec failed (%d), retry %d\n",
2768+
__func__, hwmc_ret, retry);
2769+
msleep(DS5_HW_RESET_POLL_INTERVAL_MS);
2770+
continue;
2771+
}
2772+
2773+
/* Poll status — allow both I2C and WIP retries */
2774+
{
2775+
int poll;
2776+
2777+
for (poll = 0; poll < 100; poll++) {
2778+
msleep_range(5);
2779+
hwmc_ret = ds5_read(state, DS5_HWMC_STATUS,
2780+
&hwmc_status);
2781+
if (hwmc_ret == 0 &&
2782+
hwmc_status == DS5_HWMC_STATUS_OK)
2783+
break;
2784+
}
2785+
}
2786+
2787+
if (hwmc_ret == 0 && hwmc_status == DS5_HWMC_STATUS_OK) {
2788+
dev_info(&state->client->dev,
2789+
"%s(): HWMC ready after %d ms\n",
2790+
__func__,
2791+
(retry + 1) * DS5_HW_RESET_POLL_INTERVAL_MS);
2792+
break;
2793+
}
2794+
2795+
dev_dbg(&state->client->dev,
2796+
"%s(): HWMC not ready (status: 0x%x, ret: %d), retry %d\n",
2797+
__func__, hwmc_status, hwmc_ret, retry);
2798+
msleep(DS5_HW_RESET_POLL_INTERVAL_MS);
2799+
}
2800+
2801+
if (retry >= DS5_HW_RESET_MAX_RETRIES)
2802+
dev_warn(&state->client->dev,
2803+
"%s(): HWMC subsystem not ready after %d ms, userspace queries may fail\n",
2804+
__func__, DS5_HW_RESET_TIMEOUT_MS);
2805+
}
2806+
27332807
dev_info(&state->client->dev,
27342808
"%s(): HW reset complete. Firmware: %d.%d.%d.%d\n",
27352809
__func__,
@@ -3126,24 +3200,24 @@ static int ds5_gvd(struct ds5 *state, unsigned char *data)
31263200
struct hwm_cmd cmd;
31273201
int ret = -1;
31283202
u16 length = 0;
3129-
u16 status = 2;
3130-
u8 retries = 3;
3203+
u16 status = DS5_HWMC_STATUS_WIP;
3204+
u8 retries = 20;
31313205

31323206
memcpy(&cmd, &gvd, sizeof(gvd));
31333207
ds5_raw_write_with_check(state, DS5_HWMC_DATA, &cmd, sizeof(cmd)); /* Write command data */
31343208
ds5_write_with_check(state, DS5_HWMC_EXEC, 0x01); /* execute cmd */
31353209
do {
3136-
if (retries != 3)
3137-
msleep_range(10);
3210+
if (retries != 20)
3211+
msleep_range(50);
31383212

31393213
ret = ds5_read(state, DS5_HWMC_STATUS, &status);
3140-
} while (ret && retries-- && status != 0);
3214+
} while ((ret || status == DS5_HWMC_STATUS_WIP) && retries--);
31413215

3142-
if (ret || status != 0) {
3216+
if (ret || status != DS5_HWMC_STATUS_OK) {
31433217
dev_err(&state->client->dev,
3144-
"%s(): Failed to read GVD, HWM cmd status: %x\n",
3145-
__func__, status);
3146-
return status;
3218+
"%s(): Failed to read GVD, HWM cmd status: %x, ret: %d\n",
3219+
__func__, status, ret);
3220+
return -EIO;
31473221
}
31483222

31493223
ret = ds5_raw_read(state, DS5_HWMC_RESP_LEN, &length, sizeof(length)); /* Read response length */

0 commit comments

Comments
 (0)