]> www.infradead.org Git - users/hch/misc.git/commitdiff
drm/amd/display: Fix DMUB reset sequence for DCN401
authorDillon Varone <Dillon.Varone@amd.com>
Thu, 13 Feb 2025 18:10:41 +0000 (13:10 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 5 Mar 2025 15:39:10 +0000 (10:39 -0500)
[WHY]
It should no longer use DMCUB_SOFT_RESET as it can result
in the memory request path becoming desynchronized.

[HOW]
To ensure robustness in the reset sequence:
1) Extend timeout on the "halt" command sent via gpint, and check for
controller to enter "wait" as a stronger guarantee that there are no
requests to memory still in flight.
2) Remove usage of DMCUB_SOFT_RESET
3) Rely on PSP to reset the controller safely

Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Dillon Varone <Dillon.Varone@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c
drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.h

index 39a8cb6d7523c3ad64a7d4192717958c29569111..e1c4fe1c6e3ee23b06a13de7acbf918bacbcc445 100644 (file)
@@ -63,8 +63,10 @@ static inline void dmub_dcn401_translate_addr(const union dmub_addr *addr_in,
 void dmub_dcn401_reset(struct dmub_srv *dmub)
 {
        union dmub_gpint_data_register cmd;
-       const uint32_t timeout = 30;
-       uint32_t in_reset, scratch, i;
+       const uint32_t timeout_us = 1 * 1000 * 1000; //1s
+       const uint32_t poll_delay_us = 1; //1us
+       uint32_t i = 0;
+       uint32_t in_reset, scratch, pwait_mode;
 
        REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &in_reset);
 
@@ -75,32 +77,35 @@ void dmub_dcn401_reset(struct dmub_srv *dmub)
 
                dmub->hw_funcs.set_gpint(dmub, cmd);
 
-               /**
-                * Timeout covers both the ACK and the wait
-                * for remaining work to finish.
-                *
-                * This is mostly bound by the PHY disable sequence.
-                * Each register check will be greater than 1us, so
-                * don't bother using udelay.
-                */
-
-               for (i = 0; i < timeout; ++i) {
+               for (i = 0; i < timeout_us; i++) {
                        if (dmub->hw_funcs.is_gpint_acked(dmub, cmd))
                                break;
+
+                       udelay(poll_delay_us);
                }
 
-               for (i = 0; i < timeout; ++i) {
+               for (; i < timeout_us; i++) {
                        scratch = dmub->hw_funcs.get_gpint_response(dmub);
                        if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
                                break;
+
+                       udelay(poll_delay_us);
                }
 
-               /* Force reset in case we timed out, DMCUB is likely hung. */
+               for (; i < timeout_us; i++) {
+                       REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &pwait_mode);
+                       if (pwait_mode & (1 << 0))
+                               break;
+
+                       udelay(poll_delay_us);
+               }
+       }
+
+       if (i >= timeout_us) {
+               /* timeout should never occur */
+               BREAK_TO_DEBUGGER();
        }
 
-       REG_UPDATE(DMCUB_CNTL2, DMCUB_SOFT_RESET, 1);
-       REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
-       REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);
        REG_WRITE(DMCUB_INBOX1_RPTR, 0);
        REG_WRITE(DMCUB_INBOX1_WPTR, 0);
        REG_WRITE(DMCUB_OUTBOX1_RPTR, 0);
@@ -131,7 +136,10 @@ void dmub_dcn401_backdoor_load(struct dmub_srv *dmub,
 
        dmub_dcn401_get_fb_base_offset(dmub, &fb_base, &fb_offset);
 
+       /* reset and disable DMCUB and MMHUBBUB DMUIF */
        REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
+       REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);
+       REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
 
        dmub_dcn401_translate_addr(&cw0->offset, fb_base, fb_offset, &offset);
 
@@ -151,6 +159,7 @@ void dmub_dcn401_backdoor_load(struct dmub_srv *dmub,
                        DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top,
                        DMCUB_REGION3_CW1_ENABLE, 1);
 
+       /* release DMCUB reset only to prevent premature execution */
        REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID,
                        0x20);
 }
@@ -161,7 +170,10 @@ void dmub_dcn401_backdoor_load_zfb_mode(struct dmub_srv *dmub,
 {
        union dmub_addr offset;
 
+       /* reset and disable DMCUB and MMHUBBUB DMUIF */
        REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
+       REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);
+       REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
 
        offset = cw0->offset;
 
@@ -181,6 +193,7 @@ void dmub_dcn401_backdoor_load_zfb_mode(struct dmub_srv *dmub,
                        DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top,
                        DMCUB_REGION3_CW1_ENABLE, 1);
 
+       /* release DMCUB reset only to prevent premature execution */
        REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID,
                        0x20);
 }
index 4c8843b796950bf070b3998b610ebce978bd5e24..31f95b27e227d6412fb290761293f0023d1e59e7 100644 (file)
@@ -169,7 +169,8 @@ struct dmub_srv;
        DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_INBOX0_RSP_INT_EN) \
        DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_ACK) \
        DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_STAT) \
-       DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_EN)
+       DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_EN) \
+       DMUB_SF(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS)
 
 struct dmub_srv_dcn401_reg_offset {
 #define DMUB_SR(reg) uint32_t reg;