@samitouri / QOSamiQemu / commits / e7644f123b

hw/ufs: Emulate DME_GET/SET for PA layer attributes

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 <jeuk20.kim@samsung.com>

Jeuk Kim committed Jun 26, 2026 at 16:30 UTC e7644f123b1e1332b34d794a0254122fd3d2a31c
2 files changed +52
hw/ufs/ufs.c
+40
@@ -363,8 +363,32 @@ static void ufs_process_db(UfsHc *u, uint32_t val)
363 qemu_bh_schedule(u->doorbell_bh);
364 }
365
366 +/*
367 + * Return canned PA layer attribute values. The emulated link has no PHY,
368 + * so these are purely declarative: a single lane in HS-Gear 4, FAST_MODE.
369 + */
370 +static uint32_t ufs_uic_dme_get_value(uint16_t attr_id)
371 +{
372 + switch (attr_id) {
373 + case UFS_ATTR_PA_AVAILTXDATALANES:
374 + case UFS_ATTR_PA_AVAILRXDATALANES:
375 + case UFS_ATTR_PA_CONNECTEDTXDATALANES:
376 + case UFS_ATTR_PA_CONNECTEDRXDATALANES:
377 + return 1;
378 + case UFS_ATTR_PA_MAXRXHSGEAR:
379 + case UFS_ATTR_PA_MAXRXPWMGEAR:
380 + return 4;
381 + case UFS_ATTR_PA_PWRMODE:
382 + return (1 << 4) | 1;
383 + default:
384 + return 0;
385 + }
386 +}
387 +
388 static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
389 {
390 + uint16_t attr_id;
391 +
392 trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2,
393 u->reg.ucmdarg3);
394 /*
@@ -378,6 +402,22 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
402 u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1);
403 u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
404 break;
405 + case UFS_UIC_CMD_DME_GET:
406 + case UFS_UIC_CMD_DME_PEER_GET:
407 + attr_id = (u->reg.ucmdarg1 >> 16) & 0xFFFF;
408 + u->reg.ucmdarg3 = ufs_uic_dme_get_value(attr_id);
409 + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
410 + break;
411 + case UFS_UIC_CMD_DME_SET:
412 + case UFS_UIC_CMD_DME_PEER_SET:
413 + attr_id = (u->reg.ucmdarg1 >> 16) & 0xFFFF;
414 + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
415 + /* DME_SET(PA_PWRMODE) is a power-mode-change trigger. */
416 + if (val == UFS_UIC_CMD_DME_SET && attr_id == UFS_ATTR_PA_PWRMODE) {
417 + u->reg.is = FIELD_DP32(u->reg.is, IS, UPMS, 1);
418 + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
419 + }
420 + break;
421 /*
422 * TODO: Revisit after PM implementation
423 * Power Management is not supported in current QEMU-UFS,
include/block/ufs.h
+12
@@ -628,6 +628,18 @@ enum {
628
629 #define UFS_MASK_UIC_COMMAND_RESULT 0xFF
630
631 +/*
632 + * MIPI UniPro PHY Adapter (PA) layer attribute IDs accessed via
633 + * DME_GET / DME_SET / DME_PEER_{GET,SET} UIC commands.
634 + */
635 +#define UFS_ATTR_PA_AVAILTXDATALANES 0x1520
636 +#define UFS_ATTR_PA_AVAILRXDATALANES 0x1540
637 +#define UFS_ATTR_PA_CONNECTEDTXDATALANES 0x1561
638 +#define UFS_ATTR_PA_PWRMODE 0x1571
639 +#define UFS_ATTR_PA_CONNECTEDRXDATALANES 0x1581
640 +#define UFS_ATTR_PA_MAXRXPWMGEAR 0x1586
641 +#define UFS_ATTR_PA_MAXRXHSGEAR 0x1587
642 +
643 /*
644 * Request Descriptor Definitions
645 */