@@ -497,6 +497,12 @@ struct ds5_counters {
497497static atomic_t ds5_reset_gen = ATOMIC_INIT (0 );
498498static 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