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