@samitouri / QOSamiQemu / commits / d9fe9d8e6b

hw/ufs: Add UFS Write Booster Support

Add UFS Write Booster implementation which follows UFS 4.1 Spec. Signed-off-by: Jaemyung Lee <jaemyung.lee@samsung.com> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>

Jaemyung Lee committed May 14, 2026 at 17:10 UTC d9fe9d8e6b5dcf0a6d5651600e5a9f68817c67dd
4 files changed +628 -20
hw/ufs/lu.c
+101 -1
@@ -58,13 +58,113 @@ static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense,
58 status, data_segment_length);
59 }
60
61 +#define UFS_GROUP_NUMBER_MASK 0x1F
62 +#define UFS_WB_GROUP_NUMBER_DEFAULT 0x00 /* 00000b */
63 +#define UFS_WB_GROUP_NUMBER_PINNED 0x18 /* 11000b */
64 +static bool ufs_wb_check_write_pinned(UfsHc *u, UfsRequest *req)
65 +{
66 + uint8_t cmd = req->req_upiu.sc.cdb[0];
67 + uint8_t group_number = UFS_WB_GROUP_NUMBER_DEFAULT;
68 +
69 + if (u->attributes.wb_buffer_partial_flush_mode != UFS_WB_FLUSH_PINNED) {
70 + return false;
71 + }
72 +
73 + if (cmd == WRITE_16) {
74 + group_number = req->req_upiu.sc.cdb[14] & UFS_GROUP_NUMBER_MASK;
75 +
76 + } else if (cmd == WRITE_10) {
77 + group_number = req->req_upiu.sc.cdb[6] & UFS_GROUP_NUMBER_MASK;
78 + }
79 +
80 + return (group_number == UFS_WB_GROUP_NUMBER_PINNED);
81 +}
82 +
83 +static void ufs_wb_process_write_normal(UfsHc *u, uint32_t transfered_len)
84 +{
85 + UfsWb *wb = &u->wb;
86 + uint64_t curr_bytes, used_bytes, remain_bytes;
87 +
88 + if (!wb->curr_bytes) {
89 + return;
90 + }
91 +
92 + curr_bytes = wb->curr_bytes - wb->pinned_curr_bytes;
93 + used_bytes = wb->used_bytes - wb->pinned_used_bytes;
94 +
95 + if (used_bytes >= curr_bytes) {
96 + return;
97 + }
98 +
99 + remain_bytes = curr_bytes - used_bytes;
100 + wb->used_bytes += MIN(remain_bytes, transfered_len);
101 +}
102 +
103 +#define UFS_WB_TOTAL_WRITTEN_DIV (10 * 1024 * 1024) /* 10MiB */
104 +static void ufs_wb_process_write_pinned(UfsHc *u, uint32_t transfered_len)
105 +{
106 + UfsWb *wb = &u->wb;
107 + uint64_t remain_bytes, remain_data;
108 + uint32_t total_written;
109 +
110 + if (!wb->pinned_curr_bytes) {
111 + ufs_wb_process_write_normal(u, transfered_len);
112 + return;
113 + }
114 +
115 + if (wb->pinned_used_bytes >= wb->pinned_curr_bytes) {
116 + ufs_wb_process_write_normal(u, transfered_len);
117 + return;
118 + }
119 +
120 + remain_bytes = wb->pinned_curr_bytes - wb->pinned_used_bytes;
121 + if (remain_bytes >= transfered_len) {
122 + wb->pinned_total_written_bytes += transfered_len;
123 + wb->pinned_used_bytes += transfered_len;
124 + wb->used_bytes += transfered_len;
125 + remain_data = 0;
126 +
127 + } else {
128 + wb->pinned_total_written_bytes += remain_bytes;
129 + wb->pinned_used_bytes += remain_bytes;
130 + wb->used_bytes += remain_bytes;
131 + remain_data = transfered_len - remain_bytes;
132 + }
133 +
134 + total_written = wb->pinned_total_written_bytes / UFS_WB_TOTAL_WRITTEN_DIV;
135 + u->attributes.pinned_wb_cumm_written_size = cpu_to_be32(total_written);
136 +
137 + ufs_wb_process_write_normal(u, remain_data);
138 +}
139 +
140 +static void ufs_wb_process_write_req(UfsRequest *req, uint32_t transfered_len)
141 +{
142 + UfsHc *u = req->hc;
143 +
144 + if (!u->flags.wb_en || !ufs_is_write_req(req)) {
145 + return;
146 + }
147 +
148 + if (ufs_wb_check_write_pinned(u, req)) {
149 + ufs_wb_process_write_pinned(u, transfered_len);
150 + } else {
151 + ufs_wb_process_write_normal(u, transfered_len);
152 + }
153 +
154 + ufs_wb_update_avail_buffer(u);
155 +}
156 +
157 static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
158 {
159 UfsRequest *req = scsi_req->hba_private;
160 int16_t status = scsi_req->status;
65 -
161 uint32_t transfered_len = scsi_req->cmd.xfer - resid;
162
163 + /* WB accounting should only happen for successful commands */
164 + if (status == GOOD) {
165 + ufs_wb_process_write_req(req, transfered_len);
166 + }
167 +
168 ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len,
169 transfered_len, status);
170
hw/ufs/ufs.c
+425 -19
@@ -375,7 +375,12 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
375 u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1);
376 u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
377 break;
378 - /* TODO: Revisit it when Power Management is implemented */
378 + /*
379 + * TODO: Revisit after PM implementation
380 + * Power Management is not supported in current QEMU-UFS,
381 + * So Write Booster's Flush during Hibern8 operation is also remained
382 + * as not considered.
383 + */
384 case UFS_UIC_CMD_DME_HIBER_ENTER:
385 u->reg.is = FIELD_DP32(u->reg.is, IS, UHES, 1);
386 u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
@@ -940,6 +945,32 @@ static const MemoryRegionOps ufs_mmio_ops = {
945 },
946 };
947
948 +static void ufs_wb_update_ee_status(UfsHc *u, uint16_t *ee_status)
949 +{
950 + UfsWb *wb = &u->wb;
951 + uint64_t curr_bytes = wb->curr_bytes - wb->pinned_curr_bytes;
952 + uint64_t used_bytes = wb->used_bytes - wb->pinned_used_bytes;
953 +
954 + if (curr_bytes != 0 && used_bytes >= curr_bytes) {
955 + *ee_status |= MASK_EE_WB_FLUSH_NEEDED;
956 + } else {
957 + *ee_status &= ~MASK_EE_WB_FLUSH_NEEDED;
958 + }
959 +
960 + if (u->attributes.wb_buffer_resize_hint != UFS_WB_HINT_KEEP) {
961 + *ee_status |= MASK_EE_WB_RESIZE_HINT;
962 + } else {
963 + *ee_status &= ~MASK_EE_WB_RESIZE_HINT;
964 + }
965 +
966 + if (wb->pinned_used_bytes != 0 &&
967 + wb->pinned_used_bytes >= wb->pinned_curr_bytes) {
968 + *ee_status |= MASK_EE_PINNED_WB_FULL;
969 + } else {
970 + *ee_status &= ~MASK_EE_PINNED_WB_FULL;
971 + }
972 +}
973 +
974 static void ufs_update_ee_status(UfsHc *u)
975 {
976 uint16_t ee_status = be16_to_cpu(u->attributes.exception_event_status);
@@ -958,6 +989,8 @@ static void ufs_update_ee_status(UfsHc *u)
989 ee_status &= ~MASK_EE_TOO_LOW_TEMP;
990 }
991
992 + ufs_wb_update_ee_status(u, &ee_status);
993 +
994 u->attributes.exception_event_status = cpu_to_be16(ee_status);
995 }
996
@@ -1064,11 +1097,16 @@ static const int flag_permission[UFS_QUERY_FLAG_IDN_COUNT] = {
1097 [UFS_QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ,
1098 [UFS_QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ,
1099 [UFS_QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ,
1067 - /* Write Booster is not supported */
1068 - [UFS_QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ,
1069 - [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ,
1100 + [UFS_QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
1101 + UFS_QUERY_FLAG_CLEAR | UFS_QUERY_FLAG_TOGGLE,
1102 + [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] =
1103 + UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR |
1104 + UFS_QUERY_FLAG_TOGGLE,
1105 + /* TODO: Revisit after PM implementation */
1106 [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ,
1071 - [UFS_QUERY_FLAG_IDN_UNPIN_EN] = UFS_QUERY_FLAG_READ,
1107 + [UFS_QUERY_FLAG_IDN_UNPIN_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
1108 + UFS_QUERY_FLAG_CLEAR |
1109 + UFS_QUERY_FLAG_TOGGLE,
1110 };
1111
1112 static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op)
@@ -1162,8 +1200,8 @@ static QueryRespCode ufs_write_flag_value(UfsHc *u, uint8_t idn, uint8_t value)
1200 case UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN:
1201 u->flags.wb_buffer_flush_en = value;
1202 break;
1165 - case UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8:
1166 - u->flags.wb_buffer_flush_during_hibernate = value;
1203 + case UFS_QUERY_FLAG_IDN_UNPIN_EN:
1204 + u->flags.unpin_en = value;
1205 break;
1206 default:
1207 return UFS_QUERY_RESULT_INVALID_VALUE;
@@ -1257,17 +1295,20 @@ static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
1295 [UFS_QUERY_ATTR_IDN_HID_PROG_RATIO] = UFS_QUERY_ATTR_READ,
1296 [UFS_QUERY_ATTR_IDN_HID_STATE] = UFS_QUERY_ATTR_READ,
1297 [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT] = UFS_QUERY_ATTR_READ,
1260 - [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_EN] = UFS_QUERY_ATTR_READ,
1298 + [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_EN] = UFS_QUERY_ATTR_WRITE,
1299 [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_STATUS] = UFS_QUERY_ATTR_READ,
1262 - [UFS_QUERY_ATTR_IDN_WB_BUFF_PARTIAL_FLUSH_MODE] = UFS_QUERY_ATTR_READ,
1263 - [UFS_QUERY_ATTR_IDN_MAX_FIFO_WB_PARTIAL_FLUSH_MODE] = UFS_QUERY_ATTR_READ,
1300 + [UFS_QUERY_ATTR_IDN_WB_BUFF_PARTIAL_FLUSH_MODE] =
1301 + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1302 + [UFS_QUERY_ATTR_IDN_MAX_FIFO_WB_PARTIAL_FLUSH_MODE] =
1303 + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1304 [UFS_QUERY_ATTR_IDN_CURR_FIFO_WB_PARTIAL_FLUSH_MODE] = UFS_QUERY_ATTR_READ,
1305 [UFS_QUERY_ATTR_IDN_PINNED_WB_BUFF_CURR_ALLOC_UNITS] = UFS_QUERY_ATTR_READ,
1306 [UFS_QUERY_ATTR_IDN_PINNED_WB_BUFF_AVAIL_PERCENT] = UFS_QUERY_ATTR_READ,
1307 [UFS_QUERY_ATTR_IDN_PINNED_WB_CUMM_WRITTEN_SIZE] = UFS_QUERY_ATTR_READ,
1268 - [UFS_QUERY_ATTR_IDN_PINNED_WB_NUM_ALLOC_UNITS] = UFS_QUERY_ATTR_READ,
1308 + [UFS_QUERY_ATTR_IDN_PINNED_WB_NUM_ALLOC_UNITS] =
1309 + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1310 [UFS_QUERY_ATTR_IDN_NON_PINNED_WB_MIN_NUM_ALLOC_UNITS] =
1270 - UFS_QUERY_ATTR_READ,
1311 + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1312 };
1313
1314 static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op)
@@ -1307,6 +1348,29 @@ static inline uint8_t ufs_read_device_temp(UfsHc *u)
1348 return 0;
1349 }
1350
1351 +static inline uint32_t ufs_wb_read_flush_status(UfsHc *u)
1352 +{
1353 + uint32_t value = u->attributes.wb_buffer_flush_status;
1354 +
1355 + if (value == UFS_WB_FLUSH_SUSPENDED || value == UFS_WB_FLUSH_COMPLETED ||
1356 + value == UFS_WB_FLUSH_FAILED) {
1357 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_IDLE;
1358 + }
1359 +
1360 + return value;
1361 +}
1362 +
1363 +static inline uint32_t ufs_wb_read_resize_status(UfsHc *u)
1364 +{
1365 + uint32_t value = u->attributes.wb_buffer_resize_status;
1366 +
1367 + if (value == UFS_WB_RESIZE_COMPLETED || value == UFS_WB_RESIZE_FAILED) {
1368 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_IDLE;
1369 + }
1370 +
1371 + return value;
1372 +}
1373 +
1374 static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1375 {
1376 switch (idn) {
@@ -1361,7 +1425,7 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1425 case UFS_QUERY_ATTR_IDN_THROTTLING_STATUS:
1426 return u->attributes.throttling_status;
1427 case UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS:
1364 - return u->attributes.wb_buffer_flush_status;
1428 + return ufs_wb_read_flush_status(u);
1429 case UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE:
1430 return u->attributes.available_wb_buffer_size;
1431 case UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST:
@@ -1392,10 +1456,8 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1456 return u->attributes.hid_state;
1457 case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT:
1458 return u->attributes.wb_buffer_resize_hint;
1395 - case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_EN:
1396 - return u->attributes.wb_buffer_resize_en;
1459 case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_STATUS:
1398 - return u->attributes.wb_buffer_resize_status;
1460 + return ufs_wb_read_resize_status(u);
1461 case UFS_QUERY_ATTR_IDN_WB_BUFF_PARTIAL_FLUSH_MODE:
1462 return u->attributes.wb_buffer_partial_flush_mode;
1463 case UFS_QUERY_ATTR_IDN_MAX_FIFO_WB_PARTIAL_FLUSH_MODE:
@@ -1416,6 +1478,132 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1478 return 0;
1479 }
1480
1481 +static void ufs_wb_resize_op(UfsHc *u, uint32_t value)
1482 +{
1483 + if (u->attributes.wb_buffer_resize_status == UFS_WB_RESIZE_IN_PROGRESS) {
1484 + return;
1485 + }
1486 +
1487 + if (value == UFS_WB_IDLE) {
1488 + return;
1489 + }
1490 +
1491 + u->attributes.wb_buffer_resize_en = value;
1492 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_IN_PROGRESS;
1493 + u->attributes.wb_buffer_resize_hint = UFS_WB_HINT_KEEP;
1494 +}
1495 +
1496 +void ufs_wb_update_avail_buffer(UfsHc *u)
1497 +{
1498 + UfsWb *wb = &u->wb;
1499 + uint64_t non_pinned_curr_bytes, non_pinned_used_bytes;
1500 + uint32_t units;
1501 +
1502 + units = ufs_byte_to_unit(u, wb->fifo_curr_bytes);
1503 + u->attributes.curr_fifo_wb_partial_flush_mode = cpu_to_be32(units);
1504 +
1505 + units = ufs_byte_to_unit(u, wb->pinned_curr_bytes);
1506 + u->attributes.pinned_wb_buffer_curr_alloc_units = cpu_to_be32(units);
1507 +
1508 + if (wb->pinned_curr_bytes <= wb->pinned_used_bytes)
1509 + u->attributes.pinned_wb_buffer_avail_percent = 0;
1510 + else
1511 + u->attributes.pinned_wb_buffer_avail_percent =
1512 + (wb->pinned_curr_bytes - wb->pinned_used_bytes) * 10 /
1513 + wb->pinned_curr_bytes;
1514 +
1515 + non_pinned_curr_bytes = wb->curr_bytes - wb->pinned_curr_bytes;
1516 + non_pinned_used_bytes = wb->used_bytes - wb->pinned_used_bytes;
1517 +
1518 + units = ufs_byte_to_unit(u, non_pinned_curr_bytes);
1519 + u->attributes.current_wb_buffer_size = cpu_to_be32(units);
1520 +
1521 + if (!non_pinned_curr_bytes)
1522 + u->attributes.available_wb_buffer_size = 0;
1523 + else
1524 + u->attributes.available_wb_buffer_size =
1525 + (non_pinned_curr_bytes - non_pinned_used_bytes) * 10 /
1526 + non_pinned_curr_bytes;
1527 +
1528 + assert(wb->curr_bytes >= wb->pinned_curr_bytes);
1529 + assert(wb->used_bytes >= wb->pinned_used_bytes);
1530 +}
1531 +
1532 +static void ufs_wb_sync_buffer_size(UfsHc *u)
1533 +{
1534 + UfsWb *wb = &u->wb;
1535 + uint64_t avail_bytes;
1536 +
1537 + wb->fifo_curr_bytes = MIN(wb->curr_bytes, wb->fifo_max_bytes);
1538 + avail_bytes = wb->curr_bytes - wb->used_bytes + wb->pinned_used_bytes;
1539 +
1540 + if ((u->attributes.wb_buffer_partial_flush_mode != UFS_WB_FLUSH_PINNED) ||
1541 + (wb->curr_bytes <= wb->non_pinned_min_bytes)) {
1542 + wb->pinned_curr_bytes = 0;
1543 + wb->pinned_used_bytes = 0;
1544 +
1545 + } else if (avail_bytes <= wb->pinned_max_bytes) {
1546 + wb->pinned_curr_bytes = avail_bytes;
1547 + wb->pinned_used_bytes =
1548 + MIN(wb->pinned_curr_bytes, wb->pinned_used_bytes);
1549 +
1550 + } else {
1551 + wb->pinned_curr_bytes = wb->pinned_max_bytes;
1552 + wb->pinned_used_bytes =
1553 + MIN(wb->pinned_curr_bytes, wb->pinned_used_bytes);
1554 + }
1555 +
1556 + ufs_wb_update_avail_buffer(u);
1557 +}
1558 +
1559 +static bool ufs_wb_max_fifo(UfsHc *u, uint32_t value)
1560 +{
1561 + UfsWb *wb = &u->wb;
1562 + uint64_t fifo_max_bytes = ufs_unit_to_byte(u, value);
1563 +
1564 + if (fifo_max_bytes > wb->max_bytes) {
1565 + return false;
1566 + }
1567 +
1568 + u->attributes.max_fifo_wb_partial_flush_mode = cpu_to_be32(value);
1569 + wb->fifo_max_bytes = fifo_max_bytes;
1570 + ufs_wb_sync_buffer_size(u);
1571 +
1572 + return true;
1573 +}
1574 +
1575 +static bool ufs_wb_pinned_max_size(UfsHc *u, uint32_t value)
1576 +{
1577 + UfsWb *wb = &u->wb;
1578 + uint64_t pinned_max_bytes = ufs_unit_to_byte(u, value);
1579 +
1580 + if (wb->max_bytes < wb->non_pinned_min_bytes + pinned_max_bytes) {
1581 + return false;
1582 + }
1583 +
1584 + u->attributes.pinned_wb_num_alloc_units = cpu_to_be32(value);
1585 + wb->pinned_max_bytes = pinned_max_bytes;
1586 + ufs_wb_sync_buffer_size(u);
1587 +
1588 + return true;
1589 +}
1590 +
1591 +static bool ufs_wb_pinned_min_size(UfsHc *u, uint32_t value)
1592 +{
1593 + UfsWb *wb = &u->wb;
1594 + uint64_t non_pinned_min_bytes = ufs_unit_to_byte(u, value);
1595 +
1596 + if (wb->max_bytes < non_pinned_min_bytes + wb->pinned_max_bytes) {
1597 + return false;
1598 + }
1599 +
1600 + u->attributes.non_pinned_wb_min_num_alloc_units = cpu_to_be32(value);
1601 + wb->non_pinned_min_bytes = non_pinned_min_bytes;
1602 + ufs_wb_sync_buffer_size(u);
1603 +
1604 + return true;
1605 +}
1606 +
1607 static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
1608 {
1609 switch (idn) {
@@ -1452,6 +1640,34 @@ static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
1640 case UFS_QUERY_ATTR_IDN_TIMESTAMP:
1641 u->attributes.timestamp = cpu_to_be64(value);
1642 break;
1643 + case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_EN:
1644 + if (value >= UFS_WB_RESIZE_OP_MAX) {
1645 + return UFS_QUERY_RESULT_INVALID_VALUE;
1646 + }
1647 + ufs_wb_resize_op(u, value);
1648 + break;
1649 + case UFS_QUERY_ATTR_IDN_WB_BUFF_PARTIAL_FLUSH_MODE:
1650 + if (value >= UFS_WB_FLUSH_MODE_MAX) {
1651 + return UFS_QUERY_RESULT_INVALID_VALUE;
1652 + }
1653 + u->attributes.wb_buffer_partial_flush_mode = value;
1654 + ufs_wb_sync_buffer_size(u);
1655 + break;
1656 + case UFS_QUERY_ATTR_IDN_MAX_FIFO_WB_PARTIAL_FLUSH_MODE:
1657 + if (!ufs_wb_max_fifo(u, value)) {
1658 + return UFS_QUERY_RESULT_INVALID_VALUE;
1659 + }
1660 + break;
1661 + case UFS_QUERY_ATTR_IDN_PINNED_WB_NUM_ALLOC_UNITS:
1662 + if (!ufs_wb_pinned_max_size(u, value)) {
1663 + return UFS_QUERY_RESULT_INVALID_VALUE;
1664 + }
1665 + break;
1666 + case UFS_QUERY_ATTR_IDN_NON_PINNED_WB_MIN_NUM_ALLOC_UNITS:
1667 + if (!ufs_wb_pinned_min_size(u, value)) {
1668 + return UFS_QUERY_RESULT_INVALID_VALUE;
1669 + }
1670 + break;
1671 default:
1672 g_assert_not_reached();
1673 return 0;
@@ -1870,10 +2086,136 @@ static void ufs_sendback_req(void *opaque)
2086 ufs_irq_check(u);
2087 }
2088
2089 +static inline uint64_t ufs_wb_total_flush_bytes(UfsHc *u)
2090 +{
2091 + UfsWb *wb = &u->wb;
2092 + uint64_t no_flush_bytes;
2093 +
2094 + switch (u->attributes.wb_buffer_partial_flush_mode) {
2095 + case UFS_WB_FLUSH_NONE:
2096 + no_flush_bytes = 0;
2097 + break;
2098 + case UFS_WB_FLUSH_FIFO:
2099 + no_flush_bytes = wb->fifo_curr_bytes;
2100 + break;
2101 + case UFS_WB_FLUSH_PINNED:
2102 + no_flush_bytes = (u->flags.unpin_en) ? 0 : wb->pinned_used_bytes;
2103 + break;
2104 + default:
2105 + g_assert_not_reached();
2106 + break;
2107 + }
2108 +
2109 + if (wb->used_bytes < no_flush_bytes) {
2110 + return 0;
2111 + }
2112 +
2113 + return wb->used_bytes - no_flush_bytes;
2114 +}
2115 +
2116 +#define UFS_WB_FLUSH_BYTES (4096 * 1024)
2117 +static void ufs_wb_process_flush(UfsHc *u)
2118 +{
2119 + UfsWb *wb = &u->wb;
2120 + uint64_t flush_bytes, total_flush_bytes;
2121 +
2122 + switch (u->attributes.wb_buffer_flush_status) {
2123 + case UFS_WB_FLUSH_IDLE:
2124 + case UFS_WB_FLUSH_SUSPENDED:
2125 + if (!u->flags.wb_buffer_flush_en || !ufs_wb_total_flush_bytes(u)) {
2126 + break;
2127 + }
2128 +
2129 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_IN_PROGRESS;
2130 + /* fallthrough */
2131 + case UFS_WB_FLUSH_IN_PROGRESS:
2132 + if (!u->flags.wb_buffer_flush_en) {
2133 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_SUSPENDED;
2134 + break;
2135 + }
2136 +
2137 + total_flush_bytes = ufs_wb_total_flush_bytes(u);
2138 + if (!total_flush_bytes) {
2139 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_COMPLETED;
2140 + break;
2141 + }
2142 +
2143 + /* Flush Pinned first */
2144 + if (wb->pinned_used_bytes && u->flags.unpin_en) {
2145 + flush_bytes = MIN(wb->pinned_used_bytes, UFS_WB_FLUSH_BYTES);
2146 + wb->pinned_used_bytes -= flush_bytes;
2147 + wb->used_bytes -= flush_bytes;
2148 + } else {
2149 + flush_bytes = MIN(total_flush_bytes, UFS_WB_FLUSH_BYTES);
2150 + wb->used_bytes -= flush_bytes;
2151 + }
2152 +
2153 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_COMPLETED;
2154 + /* fallthrough */
2155 + case UFS_WB_FLUSH_COMPLETED:
2156 + if (ufs_wb_total_flush_bytes(u)) {
2157 + u->attributes.wb_buffer_flush_status =
2158 + (u->flags.wb_buffer_flush_en) ? UFS_WB_FLUSH_IN_PROGRESS :
2159 + UFS_WB_FLUSH_IDLE;
2160 + }
2161 + }
2162 +}
2163 +
2164 +static void ufs_wb_process_resize(UfsHc *u)
2165 +{
2166 + UfsWb *wb = &u->wb;
2167 +
2168 + if (u->attributes.wb_buffer_resize_status != UFS_WB_RESIZE_IN_PROGRESS) {
2169 + return;
2170 + }
2171 +
2172 + switch (u->attributes.wb_buffer_resize_en) {
2173 + case UFS_WB_IDLE:
2174 + /* Do nothing. Complete resize directly. */
2175 + break;
2176 + case UFS_WB_DECREASE:
2177 + if (wb->curr_bytes <= wb->min_bytes ||
2178 + wb->curr_bytes <= wb->used_bytes) {
2179 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_FAILED;
2180 + return;
2181 + }
2182 +
2183 + if (wb->curr_bytes - wb->used_bytes >= wb->resize_bytes) {
2184 + wb->curr_bytes -= wb->resize_bytes;
2185 + } else {
2186 + wb->curr_bytes = wb->used_bytes;
2187 + }
2188 +
2189 + if (wb->curr_bytes < wb->min_bytes) {
2190 + wb->curr_bytes = wb->min_bytes;
2191 + }
2192 +
2193 + break;
2194 + case UFS_WB_INCREASE:
2195 + if (wb->curr_bytes >= wb->max_bytes) {
2196 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_FAILED;
2197 + return;
2198 + }
2199 +
2200 + wb->curr_bytes += wb->resize_bytes;
2201 + if (wb->curr_bytes >= wb->max_bytes) {
2202 + wb->curr_bytes = wb->max_bytes;
2203 + }
2204 +
2205 + break;
2206 + default:
2207 + g_assert_not_reached();
2208 + break;
2209 + }
2210 +
2211 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_COMPLETED;
2212 +}
2213 +
2214 static void ufs_process_idle(UfsHc *u)
2215 {
1875 - /* Currently do nothing */
1876 - return;
2216 + ufs_wb_process_flush(u);
2217 + ufs_wb_process_resize(u);
2218 + ufs_wb_sync_buffer_size(u);
2219 }
2220
2221 static inline bool ufs_check_idle(UfsHc *u)
@@ -1954,6 +2296,11 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp)
2296 return false;
2297 }
2298
2299 + if (u->params.wb_min_size > u->params.wb_max_size) {
2300 + error_setg(errp, "wb-min-size must be less than or equal wb-max-size");
2301 + return false;
2302 + }
2303 +
2304 return true;
2305 }
2306
@@ -1992,11 +2339,53 @@ static void ufs_init_state(UfsHc *u)
2339 }
2340 }
2341
2342 +static void ufs_wb_init(UfsHc *u)
2343 +{
2344 + UfsWb *wb = &u->wb;
2345 + uint32_t max_units = u->params.wb_max_size;
2346 + uint32_t min_units = u->params.wb_min_size;
2347 +
2348 + wb->max_bytes = ufs_unit_to_byte(u, max_units);
2349 + wb->min_bytes = ufs_unit_to_byte(u, min_units);
2350 +
2351 + wb->curr_bytes = wb->max_bytes;
2352 + wb->used_bytes = 0;
2353 + wb->resize_bytes = (wb->max_bytes - wb->min_bytes) / 10;
2354 +
2355 + u->attributes.wb_buffer_flush_status = UFS_WB_FLUSH_IDLE;
2356 + u->attributes.available_wb_buffer_size = 0xA;
2357 + u->attributes.wb_buffer_life_time_est = 0x1;
2358 + u->attributes.current_wb_buffer_size = cpu_to_be32(max_units);
2359 +
2360 + u->attributes.wb_buffer_resize_hint = UFS_WB_HINT_KEEP;
2361 + u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_IDLE;
2362 +
2363 + u->attributes.wb_buffer_partial_flush_mode = UFS_WB_FLUSH_NONE;
2364 +
2365 + u->attributes.max_fifo_wb_partial_flush_mode = cpu_to_be32(max_units);
2366 + u->attributes.curr_fifo_wb_partial_flush_mode = cpu_to_be32(max_units);
2367 +
2368 + wb->fifo_max_bytes = ufs_unit_to_byte(u, max_units);
2369 + wb->fifo_curr_bytes = ufs_unit_to_byte(u, max_units);
2370 +
2371 + u->attributes.pinned_wb_num_alloc_units = cpu_to_be32(max_units);
2372 + u->attributes.non_pinned_wb_min_num_alloc_units = 0;
2373 +
2374 + wb->pinned_curr_bytes = 0;
2375 + wb->pinned_used_bytes = 0;
2376 + wb->pinned_max_bytes = ufs_unit_to_byte(u, max_units);
2377 + wb->non_pinned_min_bytes = 0;
2378 + wb->pinned_total_written_bytes = 0;
2379 +}
2380 +
2381 static void ufs_init_hc(UfsHc *u)
2382 {
2383 uint32_t cap = 0;
2384 uint32_t mcqconfig = 0;
2385 uint32_t mcqcap = 0;
2386 + uint32_t ext_wb_sup = WB_RESIZE | WB_FIFO | WB_PINNED;
2387 + uint32_t ext_ufs_feat_sup =
2388 + UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF;
2389 int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
2390
2391 u->reg_size = pow2ceil(ufs_reg_size(u));
@@ -2058,8 +2447,13 @@ static void ufs_init_hc(UfsHc *u)
2447 UFS_DEV_LOW_TEMP_NOTIF;
2448 u->device_desc.queue_depth = u->params.nutrs;
2449 u->device_desc.product_revision_level = 0x04;
2450 + u->device_desc.extended_wb_support |= cpu_to_be16(ext_wb_sup);
2451 u->device_desc.extended_ufs_features_support =
2062 - cpu_to_be32(UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF);
2452 + cpu_to_be32((ext_ufs_feat_sup));
2453 + u->device_desc.write_booster_buffer_preserve_user_space_en = 0x01;
2454 + u->device_desc.write_booster_buffer_type = 0x01;
2455 + u->device_desc.num_shared_write_booster_buffer_alloc_units =
2456 + cpu_to_be32(u->params.wb_max_size);
2457
2458 memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
2459 u->geometry_desc.length = sizeof(GeometryDescriptor);
@@ -2075,6 +2469,14 @@ static void ufs_init_hc(UfsHc *u)
2469 0x0; /* out-of-order data transfer is not supported */
2470 u->geometry_desc.max_context_id_number = 0x5;
2471 u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001);
2472 + u->geometry_desc.write_booster_buffer_max_n_alloc_units =
2473 + cpu_to_be32(u->params.wb_max_size);
2474 + u->geometry_desc.device_max_write_booster_l_us = 0x1;
2475 + u->geometry_desc.write_booster_buffer_cap_adj_fac = 0x3;
2476 + u->geometry_desc.supported_write_booster_buffer_user_space_reduction_types =
2477 + 0x1;
2478 + u->geometry_desc.supported_write_booster_buffer_types =
2479 + 0x1; /* lu-dedicated buffer type is not supported */
2480
2481 memset(&u->attributes, 0, sizeof(u->attributes));
2482 u->attributes.max_data_in_size = 0x08;
@@ -2089,6 +2491,8 @@ static void ufs_init_hc(UfsHc *u)
2491 memset(&u->flags, 0, sizeof(u->flags));
2492 u->flags.permanently_disable_fw_update = 1;
2493
2494 + ufs_wb_init(u);
2495 +
2496 /*
2497 * The temperature value is fixed to UFS_TEMPERATURE and does not change
2498 * dynamically
@@ -2156,6 +2560,8 @@ static const Property ufs_props[] = {
2560 DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
2561 DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
2562 DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2),
2563 + DEFINE_PROP_UINT32("wb-max-size", UfsHc, params.wb_max_size, 0x400),
2564 + DEFINE_PROP_UINT32("wb-min-size", UfsHc, params.wb_min_size, 0x100),
2565 };
2566
2567 static const VMStateDescription ufs_vmstate = {
hw/ufs/ufs.h
+47
@@ -91,6 +91,8 @@ typedef struct UfsParams {
91 bool mcq; /* Multiple Command Queue support */
92 uint8_t mcq_qcfgptr; /* MCQ Queue Configuration Pointer in MCQCAP */
93 uint8_t mcq_maxq; /* MCQ Maximum number of Queues */
94 + uint32_t wb_max_size; /* WB Maximum allocation units */
95 + uint32_t wb_min_size; /* WB Minimum allocation units */
96 } UfsParams;
97
98 /*
@@ -118,6 +120,26 @@ typedef struct UfsCq {
120 QTAILQ_HEAD(, UfsRequest) req_list;
121 } UfsCq;
122
123 +/*
124 + * Extended features
125 + */
126 +typedef struct UfsWb {
127 + uint64_t max_bytes;
128 + uint64_t min_bytes;
129 + uint64_t curr_bytes;
130 + uint64_t used_bytes;
131 + uint64_t resize_bytes;
132 +
133 + uint64_t fifo_max_bytes;
134 + uint64_t fifo_curr_bytes;
135 +
136 + uint64_t pinned_max_bytes;
137 + uint64_t non_pinned_min_bytes;
138 + uint64_t pinned_curr_bytes;
139 + uint64_t pinned_used_bytes;
140 + uint64_t pinned_total_written_bytes;
141 +} UfsWb;
142 +
143 typedef struct UfsHc {
144 PCIDevice parent_obj;
145 UfsBus bus;
@@ -147,6 +169,9 @@ typedef struct UfsHc {
169 UfsSq *sq[UFS_MAX_MCQ_QNUM];
170 UfsCq *cq[UFS_MAX_MCQ_QNUM];
171
172 + /* Extended features */
173 + UfsWb wb;
174 +
175 uint8_t temperature;
176
177 QEMUTimer idle_timer;
@@ -218,6 +243,27 @@ static inline bool ufs_mcq_cq_full(UfsHc *u, uint32_t qid)
243 return tail == ufs_mcq_cq_head(u, qid);
244 }
245
246 +static inline uint64_t ufs_unit_to_byte(UfsHc *u, uint32_t unit)
247 +{
248 + return (uint64_t)unit * u->geometry_desc.allocation_unit_size *
249 + be32_to_cpu(u->geometry_desc.segment_size) * BDRV_SECTOR_SIZE;
250 +}
251 +
252 +static inline uint32_t ufs_byte_to_unit(UfsHc *u, uint64_t byte)
253 +{
254 + return byte / BDRV_SECTOR_SIZE /
255 + be32_to_cpu(u->geometry_desc.segment_size) /
256 + u->geometry_desc.allocation_unit_size;
257 +}
258 +
259 +static inline bool ufs_is_write_req(UfsRequest *req)
260 +{
261 + uint8_t cmd = req->req_upiu.sc.cdb[0];
262 +
263 + /* UFS 4.1 Specifiaction doesn't support WRITE_12 */
264 + return (cmd == WRITE_6) || (cmd == WRITE_10) || (cmd == WRITE_16);
265 +}
266 +
267 #define TYPE_UFS "ufs"
268 #define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
269
@@ -250,5 +296,6 @@ void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
296 uint16_t data_segment_length);
297 void ufs_build_query_response(UfsRequest *req);
298 void ufs_complete_req(UfsRequest *req, UfsReqResult req_result);
299 +void ufs_wb_update_avail_buffer(UfsHc *u);
300 void ufs_init_wlu(UfsLu *wlu, uint8_t wlun);
301 #endif /* HW_UFS_UFS_H */
include/block/ufs.h
+55
@@ -1126,11 +1126,24 @@ enum health_desc_param {
1126 UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
1127 };
1128
1129 +/* Possible values for bUFSFeaturesSupport */
1130 enum {
1131 UFS_DEV_HIGH_TEMP_NOTIF = BIT(4),
1132 UFS_DEV_LOW_TEMP_NOTIF = BIT(5),
1133 };
1134
1135 +/* Possible values for dExtendedWriteBoosterSupport */
1136 +enum {
1137 + WB_RESIZE = BIT(0),
1138 + WB_FIFO = BIT(1),
1139 + WB_PINNED = BIT(2),
1140 +};
1141 +
1142 +/* Possible values for dExtendedUFSFeaturesSupport */
1143 +enum {
1144 + UFS_DEV_WB_SUPPORT = BIT(8),
1145 +};
1146 +
1147 /* WriteBooster buffer mode */
1148 enum {
1149 UFS_WB_BUF_MODE_LU_DEDICATED = 0x0,
@@ -1153,6 +1166,9 @@ enum ufs_lu_wp_type {
1166 enum {
1167 MASK_EE_TOO_HIGH_TEMP = BIT(3),
1168 MASK_EE_TOO_LOW_TEMP = BIT(4),
1169 + MASK_EE_WB_FLUSH_NEEDED = BIT(5),
1170 + MASK_EE_WB_RESIZE_HINT = BIT(8),
1171 + MASK_EE_PINNED_WB_FULL = BIT(10),
1172 };
1173
1174 /* UTP QUERY Transaction Specific Fields OpCode */
@@ -1207,6 +1223,45 @@ enum ufs_dev_pwr_mode {
1223 UFS_DEEPSLEEP_PWR_MODE = 4,
1224 };
1225
1226 +/* UFS Write Booster */
1227 +enum ufs_wb_flush_status {
1228 + UFS_WB_FLUSH_IDLE = 0,
1229 + UFS_WB_FLUSH_IN_PROGRESS = 1,
1230 + UFS_WB_FLUSH_SUSPENDED = 2,
1231 + UFS_WB_FLUSH_COMPLETED = 3,
1232 + UFS_WB_FLUSH_FAILED = 4,
1233 + UFS_WB_FLUSH_STATUS_MAX,
1234 +};
1235 +
1236 +enum ufs_wb_flush_mode {
1237 + UFS_WB_FLUSH_NONE = 0,
1238 + UFS_WB_FLUSH_FIFO = 1,
1239 + UFS_WB_FLUSH_PINNED = 2,
1240 + UFS_WB_FLUSH_MODE_MAX,
1241 +};
1242 +
1243 +enum ufs_wb_resize_hint {
1244 + UFS_WB_HINT_KEEP = 0,
1245 + UFS_WB_HINT_DECREASE = 1,
1246 + UFS_WB_HINT_INCREASE = 2,
1247 + UFS_WB_RESIZE_HINT_MAX,
1248 +};
1249 +
1250 +enum ufs_wb_resize_op {
1251 + UFS_WB_IDLE = 0,
1252 + UFS_WB_DECREASE = 1,
1253 + UFS_WB_INCREASE = 2,
1254 + UFS_WB_RESIZE_OP_MAX,
1255 +};
1256 +
1257 +enum ufs_wb_resize_status {
1258 + UFS_WB_RESIZE_IDLE = 0,
1259 + UFS_WB_RESIZE_IN_PROGRESS = 1,
1260 + UFS_WB_RESIZE_COMPLETED = 2,
1261 + UFS_WB_RESIZE_FAILED = 3,
1262 + UFS_WB_RESIZE_STATUS_MAX,
1263 +};
1264 +
1265 /*
1266 * struct UtpCmdRsp - Response UPIU structure
1267 * @residual_transfer_count: Residual transfer count DW-3