drm/amd/display: Wait for clean shutdown in DMCUB reset
authorNicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Thu, 16 Jan 2020 14:16:18 +0000 (09:16 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 6 Feb 2020 20:04:37 +0000 (15:04 -0500)
[Why]
The DMCUB may be currently executing commands when the reset is
triggered.

Before issuing a reset we should first wait for the DMCUB to finish
its work.

[How]
Send the GPINT command for halting the firmware before reset.

Get the ack for the command then wait for the scratch register to
become the correct value.

We want this to take under ~40us or so at most before we force reset
to cover PHY delay sequence max time.

Each register read will be at least ~1-3us so don't bother using udelay.

Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
Acked-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h
drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c

index 5398ed6b35d1f31f75de555bace8d59ff688bc14..e13685917dab63b5c5239ee39f51675d593615ec 100644 (file)
@@ -65,4 +65,10 @@ enum dmub_gpint_command {
        DMUB_GPINT__STOP_FW = 2,
 };
 
+/**
+ * Command responses.
+ */
+
+#define DMUB_GPINT__STOP_FW_RESPONSE 0xDEADDEAD
+
 #endif /* _DMUB_GPINT_CMD_H_ */
index 7c1604c2221cc4e9d30ae5fe69328f31f3e8bd00..479f17bb3800f68729189a221bcff8ee12730dc4 100644 (file)
@@ -77,6 +77,42 @@ static inline void dmub_dcn20_translate_addr(const union dmub_addr *addr_in,
 
 void dmub_dcn20_reset(struct dmub_srv *dmub)
 {
+       union dmub_gpint_data_register cmd;
+       const uint32_t timeout = 30;
+       uint32_t in_reset, scratch, i;
+
+       REG_GET(DMCUB_CNTL, DMCUB_SOFT_RESET, &in_reset);
+
+       if (in_reset == 0) {
+               cmd.bits.status = 1;
+               cmd.bits.command_code = DMUB_GPINT__STOP_FW;
+               cmd.bits.param = 0;
+
+               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) {
+                       if (dmub->hw_funcs.is_gpint_acked(dmub, cmd))
+                               break;
+               }
+
+               for (i = 0; i < timeout; ++i) {
+                       scratch = dmub->hw_funcs.get_gpint_response(dmub);
+                       if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
+                               break;
+               }
+
+               /* Force reset in case we timed out, DMCUB is likely hung. */
+       }
+
        REG_UPDATE(DMCUB_CNTL, DMCUB_SOFT_RESET, 1);
        REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
        REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);