Skip to content

Commit 02f77ce

Browse files
committed
d4xx: fix D401 post-reset I2C instability and GMSL link degradation
D401 (FW 5.17.x) has a secondary FW initialization phase that briefly drops the I2C bus ~65ms after the initial readiness checks (Steps 7-9) pass. When userspace queries fail, the viewer triggers another HW reset every ~10s. Repeated rapid serializer re-inits progressively degrade the GMSL link until SERDES pipe setup fails and the device becomes unrecoverable without a host restart. Three fixes: 1. Step 10 - Post-reset I2C stability gate: After HWMC readiness (Step 9), verify the I2C link is sustained by performing 3 consecutive reads 200ms apart. If the link drops mid-verification, wait up to 3s for it to stabilize. 2. Reset cooldown: Track last reset timestamp and enforce a 5s minimum interval between consecutive HW resets, preventing cascading GMSL link degradation from rapid reset cycles. 3. Phase 2 SERDES escalation: If Step 10 detects persistent I2C instability despite Phase 1 having passed, escalate to Phase 2 (full deserializer reset) via a new force_phase2 parameter on ds5_hw_reset_serdes_recovery() instead of duplicating the function. Tested: D457 passes 20 HW reset cycles. D401 no longer degrades after repeated resets.
1 parent 693febd commit 02f77ce

1 file changed

Lines changed: 172 additions & 24 deletions

File tree

kernel/realsense/d4xx.c

Lines changed: 172 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@ struct ds5_counters {
497497
static atomic_t ds5_reset_gen = ATOMIC_INIT(0);
498498
static atomic_t ds5_probe_reset_once = ATOMIC_INIT(0);
499499

500+
/* Timestamp (jiffies) of last completed HW reset.
501+
* Used to enforce DS5_HW_RESET_COOLDOWN_MS between consecutive resets
502+
* and prevent GMSL link degradation from rapid reset cycles.
503+
*/
504+
static unsigned long ds5_last_reset_jiffies;
505+
500506
/* Cached device type from HW reset Step 8.
501507
* During probe the first instance resets the camera, causing DS5_DEVICE_TYPE
502508
* to temporarily return 0. Step 8 polls until the register is valid and
@@ -2303,6 +2309,22 @@ static int ds5_set_calibration_data(struct ds5 *state,
23032309
#define DS5_HW_RESET_TIMEOUT_MS 10000
23042310
#define DS5_HW_RESET_MAX_RETRIES (DS5_HW_RESET_TIMEOUT_MS / DS5_HW_RESET_POLL_INTERVAL_MS)
23052311

2312+
/* Post-reset I2C stability verification (Step 10).
2313+
* After FW reports ready and HWMC passes, verify the I2C link is
2314+
* *sustained* — some camera SKUs (D401) have a secondary FW init phase
2315+
* that briefly drops I2C ~65ms after the initial checks pass.
2316+
*/
2317+
#define DS5_HW_RESET_STABILITY_READS 3 /* consecutive successful reads */
2318+
#define DS5_HW_RESET_STABILITY_INTERVAL_MS 200 /* between each check */
2319+
#define DS5_HW_RESET_STABILITY_TIMEOUT_MS 3000 /* max wait for stable link */
2320+
2321+
/* Minimum interval between consecutive HW resets (ms).
2322+
* Rapid back-to-back resets degrade the GMSL link because each
2323+
* serializer re-init (Phase 1) conflicts with the camera FW's own
2324+
* I2C bus reconfiguration after reboot.
2325+
*/
2326+
#define DS5_HW_RESET_COOLDOWN_MS 5000
2327+
23062328
/*
23072329
* Register 0x5020 status values (from firmware):
23082330
* - 0xDEAD: Device in normal UVC mode (ready) - ICT returns error for unhandled cmd
@@ -2314,16 +2336,21 @@ static int ds5_set_calibration_data(struct ds5 *state,
23142336

23152337
/*
23162338
* ds5_hw_reset_serdes_recovery - Tiered SERDES recovery after HW reset.
2339+
* @state: Driver state
2340+
* @force_phase2: When true, skip Phase 1 and go straight to Phase 2
2341+
* (full deserializer reset). Used by Step 10 when the
2342+
* I2C link is unstable despite Phase 1 having passed.
23172343
*
23182344
* Phase 1: Re-init serializer only (per-camera, non-disruptive).
2319-
* Phase 2: If I2C still fails, perform a full deserializer reset_oneshot
2320-
* (chip-wide, disruptive) only when no sibling camera on the same
2321-
* deserializer is currently reachable over I2C and streaming.
2345+
* Phase 2: If I2C still fails (or force_phase2), perform a full deserializer
2346+
* reset_oneshot (chip-wide, disruptive) only when no sibling camera
2347+
* on the same deserializer is currently reachable over I2C and
2348+
* streaming.
23222349
*
23232350
* Returns 0 on success, negative errno on failure.
23242351
*/
23252352
#ifdef CONFIG_VIDEO_D4XX_SERDES
2326-
static int ds5_hw_reset_serdes_recovery(struct ds5 *state)
2353+
static int ds5_hw_reset_serdes_recovery(struct ds5 *state, bool force_phase2)
23272354
{
23282355
int ret;
23292356
int i;
@@ -2333,30 +2360,36 @@ static int ds5_hw_reset_serdes_recovery(struct ds5 *state)
23332360
if (!state->ser_dev || !state->dser_dev)
23342361
return 0;
23352362

2336-
/* Phase 1 — serializer-only re-init (per-camera, safe) */
2337-
dev_info(&state->client->dev,
2338-
"%s(): Phase 1 - re-initializing serializer\n", __func__);
2339-
ret = max9295_init_settings(state->ser_dev);
2340-
if (ret < 0)
2341-
dev_warn(&state->client->dev,
2342-
"%s(): serializer init_settings failed: %d\n",
2343-
__func__, ret);
2363+
if (!force_phase2) {
2364+
/* Phase 1 — serializer-only re-init (per-camera, safe) */
2365+
dev_info(&state->client->dev,
2366+
"%s(): Phase 1 - re-initializing serializer\n", __func__);
2367+
ret = max9295_init_settings(state->ser_dev);
2368+
if (ret < 0)
2369+
dev_warn(&state->client->dev,
2370+
"%s(): serializer init_settings failed: %d\n",
2371+
__func__, ret);
23442372

2345-
msleep(100);
2373+
msleep(100);
23462374

2347-
/* Verify I2C link to camera is working */
2348-
ret = ds5_read(state, DS5_FW_VERSION, &tmp);
2349-
if (ret == 0) {
2375+
/* Verify I2C link to camera is working */
2376+
ret = ds5_read(state, DS5_FW_VERSION, &tmp);
2377+
if (ret == 0) {
2378+
dev_info(&state->client->dev,
2379+
"%s(): Phase 1 succeeded, I2C link OK\n", __func__);
2380+
return 0;
2381+
}
2382+
2383+
/* Phase 2 — I2C still broken; consider full deserializer reset */
2384+
dev_warn(&state->client->dev,
2385+
"%s(): Phase 1 failed (I2C err %d), checking siblings before deser reset\n",
2386+
__func__, ret);
2387+
} else {
23502388
dev_info(&state->client->dev,
2351-
"%s(): Phase 1 succeeded, I2C link OK\n", __func__);
2352-
return 0;
2389+
"%s(): Phase 2 forced (I2C link unstable after Phase 1), checking siblings\n",
2390+
__func__);
23532391
}
23542392

2355-
/* Phase 2 — I2C still broken; consider full deserializer reset */
2356-
dev_warn(&state->client->dev,
2357-
"%s(): Phase 1 failed (I2C err %d), checking siblings before deser reset\n",
2358-
__func__, ret);
2359-
23602393
/*
23612394
* In the D4XX architecture each physical camera has 4 driver instances
23622395
* (Depth, RGB, IR, IMU) sharing the same ser_dev. A "true sibling" is
@@ -2512,6 +2545,28 @@ static int ds5_hw_reset_with_recovery(struct ds5 *state)
25122545
dev_info(&state->client->dev, "%s(): Initiating HW reset with recovery\n",
25132546
__func__);
25142547

2548+
/* 0. Reset cooldown — prevent rapid consecutive resets.
2549+
* Repeated HW reset + Phase-1 serializer re-init without letting
2550+
* the camera FW finish its post-boot I2C bus reconfiguration
2551+
* progressively degrades the GMSL link until even SERDES pipe
2552+
* setup fails. Enforce a minimum interval between resets.
2553+
* Skip check on the very first reset (ds5_last_reset_jiffies == 0).
2554+
*/
2555+
if (ds5_last_reset_jiffies) {
2556+
unsigned long elapsed = jiffies - ds5_last_reset_jiffies;
2557+
unsigned long cooldown = msecs_to_jiffies(DS5_HW_RESET_COOLDOWN_MS);
2558+
2559+
if (time_before(jiffies, ds5_last_reset_jiffies + cooldown)) {
2560+
unsigned long remaining = cooldown - elapsed;
2561+
2562+
dev_info(&state->client->dev,
2563+
"%s(): Reset cooldown — last reset %u ms ago, waiting %u ms\n",
2564+
__func__, jiffies_to_msecs(elapsed),
2565+
jiffies_to_msecs(remaining));
2566+
msleep(jiffies_to_msecs(remaining));
2567+
}
2568+
}
2569+
25152570
/* 1. Stop active streams on the device before reset.
25162571
* This ensures FW and SERDES are in a clean state.
25172572
*
@@ -2691,7 +2746,7 @@ static int ds5_hw_reset_with_recovery(struct ds5 *state)
26912746
* Phase 1: serializer-only re-init (per-camera, non-disruptive).
26922747
* Phase 2: full deserializer reset only if ALL siblings are also dead.
26932748
*/
2694-
ret = ds5_hw_reset_serdes_recovery(state);
2749+
ret = ds5_hw_reset_serdes_recovery(state, false);
26952750
if (ret < 0) {
26962751
dev_err(&state->client->dev,
26972752
"%s(): SERDES recovery failed: %d\n", __func__, ret);
@@ -2813,12 +2868,105 @@ static int ds5_hw_reset_with_recovery(struct ds5 *state)
28132868
__func__, DS5_HW_RESET_TIMEOUT_MS);
28142869
}
28152870

2871+
#ifdef CONFIG_VIDEO_D4XX_SERDES
2872+
/* 10. Post-reset I2C stability verification.
2873+
* Some camera SKUs (notably D401 with FW 5.17.x) have a secondary
2874+
* FW initialization phase that briefly drops the I2C bus ~65ms after
2875+
* the initial readiness checks (Steps 7-9) pass. If we return now,
2876+
* userspace HWMC queries NAK, the viewer triggers another reset,
2877+
* and repeated rapid resets degrade the GMSL link until SERDES pipe
2878+
* setup fails and the device is unrecoverable without a host reboot.
2879+
*
2880+
* Verify I2C stability by performing DS5_HW_RESET_STABILITY_READS
2881+
* consecutive successful reads spaced DS5_HW_RESET_STABILITY_INTERVAL_MS
2882+
* apart. If the link drops mid-verification, reset the counter and
2883+
* keep waiting (up to DS5_HW_RESET_STABILITY_TIMEOUT_MS). If it
2884+
* remains unstable, escalate to Phase 2 (full deserializer reset).
2885+
*/
2886+
{
2887+
int stable_count = 0;
2888+
unsigned long stab_ts = jiffies;
2889+
unsigned long stab_timeout =
2890+
stab_ts + msecs_to_jiffies(DS5_HW_RESET_STABILITY_TIMEOUT_MS);
2891+
u16 stab_val = 0;
2892+
2893+
while (time_before(jiffies, stab_timeout)) {
2894+
msleep(DS5_HW_RESET_STABILITY_INTERVAL_MS);
2895+
2896+
ret = ds5_read(state, DS5_FW_VERSION, &stab_val);
2897+
if (ret == 0 && stab_val != 0) {
2898+
stable_count++;
2899+
if (stable_count >= DS5_HW_RESET_STABILITY_READS) {
2900+
dev_info(&state->client->dev,
2901+
"%s(): I2C link stable after %d ms (%d consecutive reads OK)\n",
2902+
__func__,
2903+
jiffies_to_msecs(jiffies - stab_ts),
2904+
stable_count);
2905+
break;
2906+
}
2907+
} else {
2908+
if (stable_count > 0)
2909+
dev_warn(&state->client->dev,
2910+
"%s(): I2C stability check failed after %d OK reads (ret=%d, val=0x%x), resetting counter\n",
2911+
__func__, stable_count, ret, stab_val);
2912+
stable_count = 0;
2913+
}
2914+
}
2915+
2916+
if (stable_count < DS5_HW_RESET_STABILITY_READS) {
2917+
/* I2C link is unstable — escalate to Phase 2 (full deser reset) */
2918+
dev_warn(&state->client->dev,
2919+
"%s(): I2C link unstable after %d ms, escalating to Phase 2 SERDES recovery\n",
2920+
__func__,
2921+
jiffies_to_msecs(jiffies - stab_ts));
2922+
2923+
ret = ds5_hw_reset_serdes_recovery(state, true);
2924+
if (ret < 0) {
2925+
dev_err(&state->client->dev,
2926+
"%s(): Phase 2 escalation failed: %d\n",
2927+
__func__, ret);
2928+
/* Continue anyway — the device might still be partially usable */
2929+
} else {
2930+
/* Re-verify stability after Phase 2 */
2931+
stable_count = 0;
2932+
stab_ts = jiffies;
2933+
stab_timeout = stab_ts + msecs_to_jiffies(
2934+
DS5_HW_RESET_STABILITY_TIMEOUT_MS);
2935+
2936+
while (time_before(jiffies, stab_timeout)) {
2937+
msleep(DS5_HW_RESET_STABILITY_INTERVAL_MS);
2938+
ret = ds5_read(state, DS5_FW_VERSION, &stab_val);
2939+
if (ret == 0 && stab_val != 0) {
2940+
stable_count++;
2941+
if (stable_count >= DS5_HW_RESET_STABILITY_READS) {
2942+
dev_info(&state->client->dev,
2943+
"%s(): I2C link stable after Phase 2 (%d ms)\n",
2944+
__func__,
2945+
jiffies_to_msecs(jiffies - stab_ts));
2946+
break;
2947+
}
2948+
} else {
2949+
stable_count = 0;
2950+
}
2951+
}
2952+
2953+
if (stable_count < DS5_HW_RESET_STABILITY_READS)
2954+
dev_warn(&state->client->dev,
2955+
"%s(): I2C still unstable after Phase 2, proceeding anyway\n",
2956+
__func__);
2957+
}
2958+
}
2959+
}
2960+
#endif
2961+
28162962
dev_info(&state->client->dev,
28172963
"%s(): HW reset complete. Firmware: %d.%d.%d.%d\n",
28182964
__func__,
28192965
(state->fw_version >> 8) & 0xff, state->fw_version & 0xff,
28202966
(state->fw_build >> 8) & 0xff, state->fw_build & 0xff);
28212967

2968+
ds5_last_reset_jiffies = jiffies;
2969+
28222970
return 0;
28232971
}
28242972

0 commit comments

Comments
 (0)