From b1e9da7f7dd4a7bfeb8c0819cfd97db672229e42 Mon Sep 17 00:00:00 2001 From: Nicholas Kazlauskas Date: Thu, 16 Jan 2020 09:16:18 -0500 Subject: [PATCH] drm/amd/display: Wait for clean shutdown in DMCUB reset [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 Reviewed-by: Tony Cheng Acked-by: Bhawanpreet Lakha Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h | 6 ++++ drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c | 36 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h index 5398ed6..e136859 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_gpint_cmd.h @@ -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_ */ diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c index 7c1604c..479f17b 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c @@ -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); -- 2.7.4