@@ -34,6 +34,11 @@
#define UFS_MAX_NUTMRS 8
#define UFS_MCQ_QCFGPTR 2
+/* Each value represents the temperature in celsius as (value - 80) */
+#define UFS_TEMPERATURE 120
+#define UFS_TOO_HIGH_TEMP_BOUNDARY 160
+#define UFS_TOO_LOW_TEMP_BOUNDARY 60
+
static void ufs_exec_req(UfsRequest *req);
static void ufs_clear_req(UfsRequest *req);
@@ -838,6 +843,42 @@ static const MemoryRegionOps ufs_mmio_ops = {
},
};
+static void ufs_update_ee_status(UfsHc *u)
+{
+ uint16_t ee_status = be16_to_cpu(u->attributes.exception_event_status);
+ uint8_t high_temp_thresh = u->attributes.device_too_high_temp_boundary;
+ uint8_t low_temp_thresh = u->attributes.device_too_low_temp_boundary;
+
+ if (u->temperature >= high_temp_thresh) {
+ ee_status |= MASK_EE_TOO_HIGH_TEMP;
+ } else {
+ ee_status &= ~MASK_EE_TOO_HIGH_TEMP;
+ }
+
+ if (u->temperature <= low_temp_thresh) {
+ ee_status |= MASK_EE_TOO_LOW_TEMP;
+ } else {
+ ee_status &= ~MASK_EE_TOO_LOW_TEMP;
+ }
+
+ u->attributes.exception_event_status = cpu_to_be16(ee_status);
+}
+
+static bool ufs_check_exception_event_alert(UfsHc *u, uint8_t trans_type)
+{
+ uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control);
+ uint16_t ee_status;
+
+ if (trans_type != UFS_UPIU_TRANSACTION_RESPONSE) {
+ return false;
+ }
+
+ ufs_update_ee_status(u);
+
+ ee_status = be16_to_cpu(u->attributes.exception_event_status);
+
+ return ee_control & ee_status;
+}
void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
uint8_t response, uint8_t scsi_status,
@@ -848,6 +889,8 @@ void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
req->rsp_upiu.header.flags = flags;
req->rsp_upiu.header.response = response;
req->rsp_upiu.header.scsi_status = scsi_status;
+ req->rsp_upiu.header.device_inf =
+ ufs_check_exception_event_alert(req->hc, trans_type);
req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
}
@@ -1042,6 +1085,25 @@ static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
return UFS_QUERY_RESULT_SUCCESS;
}
+static inline uint8_t ufs_read_device_temp(UfsHc *u)
+{
+ uint8_t feat_sup = u->device_desc.ufs_features_support;
+ bool high_temp_sup, low_temp_sup, high_temp_en, low_temp_en;
+ uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control);
+
+ high_temp_sup = feat_sup & UFS_DEV_HIGH_TEMP_NOTIF;
+ low_temp_sup = feat_sup & UFS_DEV_LOW_TEMP_NOTIF;
+ high_temp_en = ee_control & MASK_EE_TOO_HIGH_TEMP;
+ low_temp_en = ee_control & MASK_EE_TOO_LOW_TEMP;
+
+ if ((high_temp_sup && high_temp_en) ||
+ (low_temp_sup && low_temp_en)) {
+ return u->temperature;
+ }
+
+ return 0;
+}
+
static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
{
switch (idn) {
@@ -1072,6 +1134,7 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
case UFS_QUERY_ATTR_IDN_EE_CONTROL:
return be16_to_cpu(u->attributes.exception_event_control);
case UFS_QUERY_ATTR_IDN_EE_STATUS:
+ ufs_update_ee_status(u);
return be16_to_cpu(u->attributes.exception_event_status);
case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
return be32_to_cpu(u->attributes.seconds_passed);
@@ -1086,7 +1149,8 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
return u->attributes.ref_clk_gating_wait_time;
case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
- return u->attributes.device_case_rough_temperaure;
+ u->attributes.device_case_rough_temperature = ufs_read_device_temp(u);
+ return u->attributes.device_case_rough_temperature;
case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
return u->attributes.device_too_high_temp_boundary;
case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND:
@@ -1677,8 +1741,12 @@ static void ufs_init_hc(UfsHc *u)
u->device_desc.ud_0_base_offset = 0x16;
u->device_desc.ud_config_p_length = 0x1A;
u->device_desc.device_rtt_cap = 0x02;
+ u->device_desc.ufs_features_support = UFS_DEV_HIGH_TEMP_NOTIF |
+ UFS_DEV_LOW_TEMP_NOTIF;
u->device_desc.queue_depth = u->params.nutrs;
u->device_desc.product_revision_level = 0x04;
+ u->device_desc.extended_ufs_features_support =
+ cpu_to_be32(UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF);
memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
u->geometry_desc.length = sizeof(GeometryDescriptor);
@@ -1702,9 +1770,17 @@ static void ufs_init_hc(UfsHc *u)
/* configure descriptor is not supported */
u->attributes.config_descr_lock = 0x01;
u->attributes.max_num_of_rtt = 0x02;
+ u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY;
+ u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY;
memset(&u->flags, 0, sizeof(u->flags));
u->flags.permanently_disable_fw_update = 1;
+
+ /*
+ * The temperature value is fixed to UFS_TEMPERATURE and does not change
+ * dynamically
+ */
+ u->temperature = UFS_TEMPERATURE;
}
static void ufs_realize(PCIDevice *pci_dev, Error **errp)
@@ -146,6 +146,8 @@ typedef struct UfsHc {
/* MCQ properties */
UfsSq *sq[UFS_MAX_MCQ_QNUM];
UfsCq *cq[UFS_MAX_MCQ_QNUM];
+
+ uint8_t temperature;
} UfsHc;
static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
@@ -461,7 +461,7 @@ typedef struct Attributes {
uint8_t psa_state;
uint32_t psa_data_size;
uint8_t ref_clk_gating_wait_time;
- uint8_t device_case_rough_temperaure;
+ uint8_t device_case_rough_temperature;
uint8_t device_too_high_temp_boundary;
uint8_t device_too_low_temp_boundary;
uint8_t throttling_status;
@@ -1073,6 +1073,11 @@ enum health_desc_param {
UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
};
+enum {
+ UFS_DEV_HIGH_TEMP_NOTIF = BIT(4),
+ UFS_DEV_LOW_TEMP_NOTIF = BIT(5),
+};
+
/* WriteBooster buffer mode */
enum {
UFS_WB_BUF_MODE_LU_DEDICATED = 0x0,
@@ -1091,6 +1096,12 @@ enum ufs_lu_wp_type {
UFS_LU_PERM_WP = 0x02,
};
+/* Exception event mask values */
+enum {
+ MASK_EE_TOO_HIGH_TEMP = BIT(3),
+ MASK_EE_TOO_LOW_TEMP = BIT(4),
+};
+
/* UTP QUERY Transaction Specific Fields OpCode */
enum query_opcode {
UFS_UPIU_QUERY_OPCODE_NOP = 0x0,
This patch introduces temperature event notification support to the UFS emulation. It enables the emulated UFS device to generate temperature-related events, including high and low temperature notifications, in compliance with the UFS specification. With this feature, UFS drivers can now handle temperature exception events during testing and development within the emulated environment. This enhances validation and debugging capabilities for thermal event handling in UFS implementations. Signed-off-by: Keoseong Park <keosung.park@samsung.com> --- hw/ufs/ufs.c | 78 ++++++++++++++++++++++++++++++++++++++++++++- hw/ufs/ufs.h | 2 ++ include/block/ufs.h | 13 +++++++- 3 files changed, 91 insertions(+), 2 deletions(-)