@@ -1559,3 +1559,24 @@ Description:
Symbol - HCMID. This file shows the UFSHCD manufacturer id.
The Manufacturer ID is defined by JEDEC in JEDEC-JEP106.
The file is read only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/device_lvl_exception
+What: /sys/bus/platform/devices/*.ufs/device_lvl_exception
+Date: February 2025
+Contact: Bao D. Nguyen <quic_nguyenb@quicinc.com>
+Description:
+ Indicates the number times the device level exceptions have occurred
+ since the last device reset. Read the device_lvl_exception_id to know
+ more information about the exception id.
+ The file is read only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/device_lvl_exception_id
+What: /sys/bus/platform/devices/*.ufs/device_lvl_exception_id
+Date: February 2025
+Contact: Bao D. Nguyen <quic_nguyenb@quicinc.com>
+Description:
+ This is the device JEDEC standard qDeviceLevelExceptionID attribute.
+ The definition of the qDeviceLevelExceptionID is the ufs device vendor specific.
+ Refer to the device manufacturer datasheet for more information
+ on the meaning of the qDeviceLevelExceptionID attribute value.
+ The file is read only.
@@ -458,6 +458,25 @@ static ssize_t pm_qos_enable_store(struct device *dev,
return count;
}
+static ssize_t device_lvl_exception_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", hba->dev_lvl_exception_count);
+}
+
+static ssize_t device_lvl_exception_id_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ hba->dev_lvl_exception_count = 0;
+ return sysfs_emit(buf, "%llu\n", hba->dev_lvl_exception_id);
+}
+
static DEVICE_ATTR_RW(rpm_lvl);
static DEVICE_ATTR_RO(rpm_target_dev_state);
static DEVICE_ATTR_RO(rpm_target_link_state);
@@ -470,6 +489,8 @@ static DEVICE_ATTR_RW(enable_wb_buf_flush);
static DEVICE_ATTR_RW(wb_flush_threshold);
static DEVICE_ATTR_RW(rtc_update_ms);
static DEVICE_ATTR_RW(pm_qos_enable);
+static DEVICE_ATTR_RO(device_lvl_exception);
+static DEVICE_ATTR_RO(device_lvl_exception_id);
static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
&dev_attr_rpm_lvl.attr,
@@ -484,6 +505,8 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
&dev_attr_wb_flush_threshold.attr,
&dev_attr_rtc_update_ms.attr,
&dev_attr_pm_qos_enable.attr,
+ &dev_attr_device_lvl_exception.attr,
+ &dev_attr_device_lvl_exception_id.attr,
NULL
};
@@ -5994,6 +5994,54 @@ static void ufshcd_temp_exception_event_handler(struct ufs_hba *hba, u16 status)
*/
}
+static int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba,
+ u64 *exception_id)
+{
+ struct ufs_query_req *request = NULL;
+ struct ufs_query_res *response = NULL;
+ struct utp_upiu_query_response_v4_0 *upiu_resp;
+ int err;
+
+ ufshcd_hold(hba);
+
+ mutex_lock(&hba->dev_cmd.lock);
+
+ ufshcd_init_query(hba, &request, &response,
+ UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID, 0, 0);
+
+ request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
+
+ err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, QUERY_REQ_TIMEOUT);
+
+ if (err) {
+ dev_err(hba->dev, "%s: failed to read device level exception %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ upiu_resp = (struct utp_upiu_query_response_v4_0 *)response;
+ *exception_id = be64_to_cpu(upiu_resp->value);
+out:
+ mutex_unlock(&hba->dev_cmd.lock);
+ ufshcd_release(hba);
+
+ return err;
+}
+
+static void ufshcd_device_lvl_exception_event_handler(struct ufs_hba *hba)
+{
+ u64 *exception_id;
+ int err;
+
+ hba->dev_lvl_exception_count++;
+ exception_id = &hba->dev_lvl_exception_id;
+ err = ufshcd_read_device_lvl_exception_id(hba, exception_id);
+ if (err)
+ dev_err(hba->dev, "%s: read dev lvl exception id err=%d\n",
+ __func__, err);
+}
+
static int __ufshcd_wb_toggle(struct ufs_hba *hba, bool set, enum flag_idn idn)
{
u8 index;
@@ -6216,6 +6264,9 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
if (status & hba->ee_drv_mask & MASK_EE_URGENT_TEMP)
ufshcd_temp_exception_event_handler(hba, status);
+ if (status & hba->ee_drv_mask & MASK_EE_DEV_LVL_EXCEPTION)
+ ufshcd_device_lvl_exception_event_handler(hba);
+
ufs_debugfs_exception_event(hba, status);
}
@@ -8115,6 +8166,22 @@ static void ufshcd_temp_notif_probe(struct ufs_hba *hba, const u8 *desc_buf)
}
}
+static void ufshcd_device_lvl_exception_probe(struct ufs_hba *hba, u8 *desc_buf)
+{
+ u32 ext_ufs_feature;
+
+ if (hba->dev_info.wspecversion < 0x410)
+ return;
+
+ ext_ufs_feature = get_unaligned_be32(desc_buf +
+ DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP);
+ if (!(ext_ufs_feature & UFS_DEV_LVL_EXCEPTION_SUP))
+ return;
+
+ hba->dev_lvl_exception_count = 0;
+ ufshcd_enable_ee(hba, MASK_EE_DEV_LVL_EXCEPTION);
+}
+
static void ufshcd_set_rtt(struct ufs_hba *hba)
{
struct ufs_dev_info *dev_info = &hba->dev_info;
@@ -8310,6 +8377,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
ufs_init_rtc(hba, desc_buf);
+ ufshcd_device_lvl_exception_probe(hba, desc_buf);
+
/*
* ufshcd_read_string_desc returns size of the string
* reset the error value
@@ -143,6 +143,14 @@ struct utp_upiu_query_v4_0 {
__be32 reserved;
};
+struct utp_upiu_query_response_v4_0 {
+ __u8 opcode;
+ __u8 idn;
+ __u8 index;
+ __u8 selector;
+ __be64 value;
+};
+
/**
* struct utp_upiu_cmd - Command UPIU structure
* @exp_data_transfer_len: Data Transfer Length DW-3
@@ -180,7 +180,8 @@ enum attr_idn {
QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D,
QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
- QUERY_ATTR_IDN_TIMESTAMP = 0x30
+ QUERY_ATTR_IDN_TIMESTAMP = 0x30,
+ QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID = 0x34
};
/* Descriptor idn for Query requests */
@@ -390,6 +391,7 @@ enum {
UFS_DEV_EXT_TEMP_NOTIF = BIT(6),
UFS_DEV_HPB_SUPPORT = BIT(7),
UFS_DEV_WRITE_BOOSTER_SUP = BIT(8),
+ UFS_DEV_LVL_EXCEPTION_SUP = BIT(12),
};
#define UFS_DEV_HPB_SUPPORT_VERSION 0x310
@@ -419,6 +421,7 @@ enum {
MASK_EE_TOO_LOW_TEMP = BIT(4),
MASK_EE_WRITEBOOSTER_EVENT = BIT(5),
MASK_EE_PERFORMANCE_THROTTLING = BIT(6),
+ MASK_EE_DEV_LVL_EXCEPTION = BIT(7),
};
#define MASK_EE_URGENT_TEMP (MASK_EE_TOO_HIGH_TEMP | MASK_EE_TOO_LOW_TEMP)
@@ -962,6 +962,10 @@ enum ufshcd_mcq_opr {
* @ufs_rtc_update_work: A work for UFS RTC periodic update
* @pm_qos_req: PM QoS request handle
* @pm_qos_enabled: flag to check if pm qos is enabled
+ * @dev_lvl_exception_count: Number of device level exception events
+ * between device level exception id reads.
+ * @dev_lvl_exception_id: vendor specific information about the
+ * device level exception event.
*/
struct ufs_hba {
void __iomem *mmio_base;
@@ -1130,6 +1134,9 @@ struct ufs_hba {
struct delayed_work ufs_rtc_update_work;
struct pm_qos_request pm_qos_req;
bool pm_qos_enabled;
+
+ u32 dev_lvl_exception_count;
+ u64 dev_lvl_exception_id;
};
/**
The device JEDEC standard version 4.1 adds support for the device level exception events. In order to provide the user space access to the device level exception information, we propose exposing 2 new sysfs nodes namely /sys/bus/platform/drivers/ufshcd/*/device_lvl_exception /sys/bus/platform/drivers/ufshcd/*/device_lvl_exception_id The device_lvl_exception sysfs node would report the number of device level exceptions that have occurred between the device_lvl_exception_id reads. The device_lvl_exception_id sysfs node would report the last read of the exception ID which is the JEDEC standard qDeviceLevelExceptionID attribute. The user space would query the sysfs nodes to get more information about the device level exception. Alternatively, we can change the implementation to notify the user space of the exception. Signed-off-by: Bao D. Nguyen <quic_nguyenb@quicinc.com> --- Documentation/ABI/testing/sysfs-driver-ufs | 21 +++++++++ drivers/ufs/core/ufs-sysfs.c | 23 ++++++++++ drivers/ufs/core/ufshcd.c | 69 ++++++++++++++++++++++++++++++ include/uapi/scsi/scsi_bsg_ufs.h | 8 ++++ include/ufs/ufs.h | 5 ++- include/ufs/ufshcd.h | 7 +++ 6 files changed, 132 insertions(+), 1 deletion(-)