After DME_LINK_STARTUP a UFSHCI host typically negotiates the link power
mode: it reads PA layer attributes (connected RX/TX data lanes, max RX
HS/PWM gears) via DME_GET and then issues DME_SET(PA_PWRMODE), waiting for
the UIC power-mode-change completion (IS.UPMS / HCS.UPMCRS). The device
only handled DME_LINK_STARTUP and DME_HIBER_{ENTER,EXIT} and returned
FAILURE for every other DME command, so a host that performs power-mode
change could never complete it.Return canned PA attribute values (1 lane, HS-G4, FAST_MODE) on DME_GET/PEER_GET and acknowledge DME_SET/PEER_SET. For DME_SET(PA_PWRMODE) also raise IS.UPMS and set HCS.UPMCRS=PWR_LOCAL so the power-mode change completes. The emulated link has no PHY, so no state is persisted. For example, the Linux ufshcd driver reads these attributes during probe and otherwise aborts with "invalid connected lanes value". Signed-off-by: Jeuk Kim <[email protected]> --- hw/ufs/ufs.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/block/ufs.h | 12 ++++++++++++ 2 files changed, 52 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 35c2444385..ee7eba6793 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -363,8 +363,32 @@ static void ufs_process_db(UfsHc *u, uint32_t val) qemu_bh_schedule(u->doorbell_bh); } +/* + * Return canned PA layer attribute values. The emulated link has no PHY, + * so these are purely declarative: a single lane in HS-Gear 4, FAST_MODE. + */ +static uint32_t ufs_uic_dme_get_value(uint16_t attr_id) +{ + switch (attr_id) { + case UFS_ATTR_PA_AVAILTXDATALANES: + case UFS_ATTR_PA_AVAILRXDATALANES: + case UFS_ATTR_PA_CONNECTEDTXDATALANES: + case UFS_ATTR_PA_CONNECTEDRXDATALANES: + return 1; + case UFS_ATTR_PA_MAXRXHSGEAR: + case UFS_ATTR_PA_MAXRXPWMGEAR: + return 4; + case UFS_ATTR_PA_PWRMODE: + return (1 << 4) | 1; + default: + return 0; + } +} + static void ufs_process_uiccmd(UfsHc *u, uint32_t val) { + uint16_t attr_id; + trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2, u->reg.ucmdarg3); /* @@ -378,6 +402,22 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val) u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1); u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; break; + case UFS_UIC_CMD_DME_GET: + case UFS_UIC_CMD_DME_PEER_GET: + attr_id = (u->reg.ucmdarg1 >> 16) & 0xFFFF; + u->reg.ucmdarg3 = ufs_uic_dme_get_value(attr_id); + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; + break; + case UFS_UIC_CMD_DME_SET: + case UFS_UIC_CMD_DME_PEER_SET: + attr_id = (u->reg.ucmdarg1 >> 16) & 0xFFFF; + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; + /* DME_SET(PA_PWRMODE) is a power-mode-change trigger. */ + if (val == UFS_UIC_CMD_DME_SET && attr_id == UFS_ATTR_PA_PWRMODE) { + u->reg.is = FIELD_DP32(u->reg.is, IS, UPMS, 1); + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL); + } + break; /* * TODO: Revisit after PM implementation * Power Management is not supported in current QEMU-UFS, diff --git a/include/block/ufs.h b/include/block/ufs.h index 6dd91181e5..66e8f35c0e 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -628,6 +628,18 @@ enum { #define UFS_MASK_UIC_COMMAND_RESULT 0xFF +/* + * MIPI UniPro PHY Adapter (PA) layer attribute IDs accessed via + * DME_GET / DME_SET / DME_PEER_{GET,SET} UIC commands. + */ +#define UFS_ATTR_PA_AVAILTXDATALANES 0x1520 +#define UFS_ATTR_PA_AVAILRXDATALANES 0x1540 +#define UFS_ATTR_PA_CONNECTEDTXDATALANES 0x1561 +#define UFS_ATTR_PA_PWRMODE 0x1571 +#define UFS_ATTR_PA_CONNECTEDRXDATALANES 0x1581 +#define UFS_ATTR_PA_MAXRXPWMGEAR 0x1586 +#define UFS_ATTR_PA_MAXRXHSGEAR 0x1587 + /* * Request Descriptor Definitions */ -- 2.43.0
