Skip to content

Commit bbb0814

Browse files
authored
Add hardware reset functionality with recovery for GMSL connections (#347)
Tracked-on RSDSO 21089: Device did not reset after executing the hardware reset script
1 parent 35e34ed commit bbb0814

1 file changed

Lines changed: 179 additions & 3 deletions

File tree

kernel/realsense/d4xx.c

Lines changed: 179 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ static const struct hwm_cmd ewb = {
329329
.magic_word = 0xCDAB,
330330
.opcode = 0x18,
331331
};
332+
333+
static const struct hwm_cmd cmd_hw_reset = {
334+
.header = 0x14,
335+
.magic_word = 0xCDAB,
336+
.opcode = 0x20, /* HW reset opcode */
337+
};
332338
#ifdef CONFIG_VIDEO_INTEL_IPU6
333339
static const s64 link_freq_menu_items[] = {
334340
D4XX_LINK_FREQ_360MHZ,
@@ -2141,6 +2147,9 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val)
21412147
*/
21422148
#define DS5_CAMERA_CID_HWMC_RW (DS5_CAMERA_CID_BASE+32)
21432149

2150+
/* HW reset with recovery for GMSL connections */
2151+
#define DS5_CAMERA_CID_HW_RESET (DS5_CAMERA_CID_BASE+33)
2152+
21442153
#define DS5_HWMC_DATA 0x4900
21452154
#define DS5_HWMC_STATUS 0x4904
21462155
#define DS5_HWMC_RESP_LEN 0x4908
@@ -2277,6 +2286,146 @@ static int ds5_set_calibration_data(struct ds5 *state,
22772286
return ret;
22782287
}
22792288

2289+
/* HW reset timeout and polling parameters */
2290+
#define DS5_HW_RESET_INITIAL_DELAY_MS 100
2291+
#define DS5_HW_RESET_POLL_INTERVAL_MS 200
2292+
#define DS5_HW_RESET_TIMEOUT_MS 10000
2293+
#define DS5_HW_RESET_MAX_RETRIES (DS5_HW_RESET_TIMEOUT_MS / DS5_HW_RESET_POLL_INTERVAL_MS)
2294+
2295+
/*
2296+
* Register 0x5020 status values (from firmware):
2297+
* - 0xDEAD: Device in normal UVC mode (ready) - ICT returns error for unhandled cmd
2298+
* - 0x04030201: Device in DFU mode (DFU magic bytes, little-endian)
2299+
* - I2C error: Device not yet responding (still resetting)
2300+
*/
2301+
#define DS5_HW_RESET_STATUS_READY 0xDEAD
2302+
#define DS5_HW_RESET_DFU_MAGIC_LSW 0x0201 /* Lower 16 bits of 0x04030201 */
2303+
2304+
/*
2305+
* ds5_hw_reset_with_recovery - Perform hardware reset with GMSL recovery
2306+
* @state: Driver state structure
2307+
*
2308+
* This function sends a hardware reset command to the D4XX device and
2309+
* waits for it to come back online. For GMSL connections, unlike USB,
2310+
* there is no automatic re-enumeration, so we must poll the device
2311+
* until it becomes responsive again.
2312+
*
2313+
* Returns 0 on success, negative error code on failure.
2314+
*/
2315+
static int ds5_hw_reset_with_recovery(struct ds5 *state)
2316+
{
2317+
int ret;
2318+
int retry;
2319+
u16 status = 0;
2320+
bool device_went_down = false;
2321+
2322+
dev_info(&state->client->dev, "%s(): Initiating HW reset with recovery\n",
2323+
__func__);
2324+
2325+
/* 1. Send HW reset command */
2326+
ret = ds5_raw_write(state, DS5_HWMC_DATA, &cmd_hw_reset, sizeof(cmd_hw_reset));
2327+
if (ret < 0) {
2328+
dev_err(&state->client->dev, "%s(): Failed to write HW reset command: %d\n",
2329+
__func__, ret);
2330+
return ret;
2331+
}
2332+
2333+
ret = ds5_write(state, DS5_HWMC_EXEC, 0x01);
2334+
if (ret < 0) {
2335+
dev_err(&state->client->dev, "%s(): Failed to execute HW reset command: %d\n",
2336+
__func__, ret);
2337+
return ret;
2338+
}
2339+
2340+
dev_info(&state->client->dev, "%s(): HW reset command sent, waiting for device...\n",
2341+
__func__);
2342+
2343+
/* 2. Brief delay to allow reset to begin */
2344+
msleep(DS5_HW_RESET_INITIAL_DELAY_MS);
2345+
2346+
/* 3. Poll for device to come back online */
2347+
for (retry = 0; retry < DS5_HW_RESET_MAX_RETRIES; retry++) {
2348+
msleep(DS5_HW_RESET_POLL_INTERVAL_MS);
2349+
2350+
ret = ds5_read(state, 0x5020, &status);
2351+
2352+
if (ret < 0) {
2353+
/* I2C failed - device is resetting (expected during reset) */
2354+
device_went_down = true;
2355+
dev_dbg(&state->client->dev,
2356+
"%s(): Device not responding (resetting), retry %d\n",
2357+
__func__, retry);
2358+
continue;
2359+
}
2360+
2361+
/* I2C succeeded - check status */
2362+
if (status == DS5_HW_RESET_STATUS_READY) {
2363+
/* 0xDEAD means device is in normal UVC mode (ready) */
2364+
dev_info(&state->client->dev,
2365+
"%s(): Device ready after %d ms, status: 0x%04x\n",
2366+
__func__,
2367+
DS5_HW_RESET_INITIAL_DELAY_MS +
2368+
(retry + 1) * DS5_HW_RESET_POLL_INTERVAL_MS,
2369+
status);
2370+
break;
2371+
}
2372+
2373+
if (status == DS5_HW_RESET_DFU_MAGIC_LSW) {
2374+
/* 0x0201 is lower 16-bits of DFU magic 0x04030201 */
2375+
dev_warn(&state->client->dev,
2376+
"%s(): Device in DFU/recovery mode after reset\n", __func__);
2377+
state->dfu_dev.dfu_state_flag = DS5_DFU_RECOVERY;
2378+
return 0;
2379+
}
2380+
2381+
/* Other status - keep waiting */
2382+
dev_dbg(&state->client->dev,
2383+
"%s(): Unexpected status 0x%04x, retry %d\n",
2384+
__func__, status, retry);
2385+
}
2386+
2387+
if (retry >= DS5_HW_RESET_MAX_RETRIES) {
2388+
dev_err(&state->client->dev,
2389+
"%s(): Device did not become ready after %d ms (last status: 0x%04x, i2c ret: %d)\n",
2390+
__func__, DS5_HW_RESET_INITIAL_DELAY_MS + DS5_HW_RESET_TIMEOUT_MS,
2391+
status, ret);
2392+
return -ETIMEDOUT;
2393+
}
2394+
2395+
#ifdef CONFIG_VIDEO_D4XX_SERDES
2396+
/* 4. Re-initialize SERDES link if available */
2397+
if (state->dser_dev) {
2398+
dev_info(&state->client->dev,
2399+
"%s(): Re-initializing SERDES link\n", __func__);
2400+
max9296_reset_oneshot(state->dser_dev);
2401+
msleep(300);
2402+
}
2403+
#endif
2404+
2405+
/* 5. Verify device is operational by reading firmware version */
2406+
ret = ds5_read(state, DS5_FW_VERSION, &state->fw_version);
2407+
if (ret < 0) {
2408+
dev_err(&state->client->dev,
2409+
"%s(): Failed to read firmware version: %d\n", __func__, ret);
2410+
return ret;
2411+
}
2412+
2413+
ret = ds5_read(state, DS5_FW_BUILD, &state->fw_build);
2414+
if (ret < 0) {
2415+
dev_err(&state->client->dev,
2416+
"%s(): Failed to read firmware build: %d\n", __func__, ret);
2417+
return ret;
2418+
}
2419+
2420+
dev_info(&state->client->dev,
2421+
"%s(): HW reset complete. Firmware: %d.%d.%d.%d\n",
2422+
__func__,
2423+
(state->fw_version >> 8) & 0xff, state->fw_version & 0xff,
2424+
(state->fw_build >> 8) & 0xff, state->fw_build & 0xff);
2425+
2426+
return 0;
2427+
}
2428+
22802429
static int ds5_mux_s_stream(struct v4l2_subdev *sd, int on);
22812430

22822431
static int ds5_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -2576,12 +2725,26 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl)
25762725
break;
25772726
case DS5_CAMERA_CID_HWMC_RW:
25782727
if (ctrl->p_new.p_u8) {
2728+
struct hwm_cmd *cmd = (struct hwm_cmd *)ctrl->p_new.p_u8;
25792729
u16 size = *((u8 *)ctrl->p_new.p_u8 + 1) << 8;
25802730
size |= *((u8 *)ctrl->p_new.p_u8 + 0);
2581-
ret = ds5_send_hwmc(state, size + 4,
2582-
(struct hwm_cmd *)ctrl->p_new.p_u8);
2731+
2732+
/* Check if this is a HW reset command (opcode 0x20) */
2733+
if (cmd->opcode == 0x20) {
2734+
dev_info(&state->client->dev,
2735+
"%s(): HW reset detected via HWMC_RW, using recovery path\n",
2736+
__func__);
2737+
ret = ds5_hw_reset_with_recovery(state);
2738+
} else {
2739+
ret = ds5_send_hwmc(state, size + 4, cmd);
2740+
}
25832741
}
25842742
break;
2743+
case DS5_CAMERA_CID_HW_RESET:
2744+
dev_info(&state->client->dev, "%s(): HW reset requested via V4L2 control\n",
2745+
__func__);
2746+
ret = ds5_hw_reset_with_recovery(state);
2747+
break;
25852748
case DS5_CAMERA_CID_PWM:
25862749
if (state->is_depth)
25872750
ret = ds5_write(state, base | DS5_PWM_FREQUENCY, ctrl->val);
@@ -3161,6 +3324,18 @@ static const struct v4l2_ctrl_config ds5_ctrl_hwmc_rw = {
31613324
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
31623325
};
31633326

3327+
static const struct v4l2_ctrl_config ds5_ctrl_hw_reset = {
3328+
.ops = &ds5_ctrl_ops,
3329+
.id = DS5_CAMERA_CID_HW_RESET,
3330+
.name = "HW Reset",
3331+
.type = V4L2_CTRL_TYPE_BUTTON,
3332+
.min = 0,
3333+
.max = 1,
3334+
.step = 1,
3335+
.def = 0,
3336+
.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
3337+
};
3338+
31643339
static const struct v4l2_ctrl_config ds5_ctrl_pwm = {
31653340
.ops = &ds5_ctrl_ops,
31663341
.id = DS5_CAMERA_CID_PWM,
@@ -3908,6 +4083,7 @@ static int ds5_ctrl_init(struct ds5 *state, int sid)
39084083
ctrls->ewb = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_ewb, sensor);
39094084
ctrls->hwmc = v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc, sensor);
39104085
v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hwmc_rw, sensor);
4086+
v4l2_ctrl_new_custom(hdl, &ds5_ctrl_hw_reset, sensor);
39114087
}
39124088
// DEPTH custom
39134089
if (sid == DEPTH_SID)
@@ -6080,4 +6256,4 @@ MODULE_AUTHOR("Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>,\n\
60806256
Shikun Ding <shikun.ding@intel.com>,\n\
60816257
Dmitry Perchanov <dmitry.perchanov@intel.com>");
60826258
MODULE_LICENSE("GPL v2");
6083-
MODULE_VERSION("1.0.2.4");
6259+
MODULE_VERSION("1.0.2.5");

0 commit comments

Comments
 (0)