drm/amd/display: Fix DMUB reset sequence for DCN401
[ Upstream commit 0dfcc2bf269010a6e093793034c048049a40ee93 ] [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> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
682c4226f3
commit
e015cef8b7
@@ -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)
|
void dmub_dcn401_reset(struct dmub_srv *dmub)
|
||||||
{
|
{
|
||||||
union dmub_gpint_data_register cmd;
|
union dmub_gpint_data_register cmd;
|
||||||
const uint32_t timeout = 30;
|
const uint32_t timeout_us = 1 * 1000 * 1000; //1s
|
||||||
uint32_t in_reset, scratch, i;
|
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);
|
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);
|
dmub->hw_funcs.set_gpint(dmub, cmd);
|
||||||
|
|
||||||
/**
|
for (i = 0; i < timeout_us; i++) {
|
||||||
* 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))
|
if (dmub->hw_funcs.is_gpint_acked(dmub, cmd))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
udelay(poll_delay_us);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < timeout; ++i) {
|
for (; i < timeout_us; i++) {
|
||||||
scratch = dmub->hw_funcs.get_gpint_response(dmub);
|
scratch = dmub->hw_funcs.get_gpint_response(dmub);
|
||||||
if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
|
if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
|
||||||
break;
|
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_RPTR, 0);
|
||||||
REG_WRITE(DMCUB_INBOX1_WPTR, 0);
|
REG_WRITE(DMCUB_INBOX1_WPTR, 0);
|
||||||
REG_WRITE(DMCUB_OUTBOX1_RPTR, 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);
|
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(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);
|
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_TOP_ADDRESS, cw1->region.top,
|
||||||
DMCUB_REGION3_CW1_ENABLE, 1);
|
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,
|
REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID,
|
||||||
0x20);
|
0x20);
|
||||||
}
|
}
|
||||||
@@ -161,7 +170,10 @@ void dmub_dcn401_backdoor_load_zfb_mode(struct dmub_srv *dmub,
|
|||||||
{
|
{
|
||||||
union dmub_addr offset;
|
union dmub_addr offset;
|
||||||
|
|
||||||
|
/* reset and disable DMCUB and MMHUBBUB DMUIF */
|
||||||
REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
|
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;
|
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_TOP_ADDRESS, cw1->region.top,
|
||||||
DMCUB_REGION3_CW1_ENABLE, 1);
|
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,
|
REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID,
|
||||||
0x20);
|
0x20);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_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_ACK) \
|
||||||
DMUB_SF(HOST_INTERRUPT_CSR, HOST_REG_OUTBOX0_RDY_INT_STAT) \
|
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 {
|
struct dmub_srv_dcn401_reg_offset {
|
||||||
#define DMUB_SR(reg) uint32_t reg;
|
#define DMUB_SR(reg) uint32_t reg;
|
||||||
|
|||||||
Reference in New Issue
Block a user