[PATCH v3] nvme-hwmon: add temperature threshold hysteresis feature

Tokunori Ikegami ikegami.t at gmail.com
Sat Nov 9 05:33:21 PST 2024


The definition TMPTHH field supported by NVMe 2.1.

Signed-off-by: Tokunori Ikegami <ikegami.t at gmail.com>
---
 drivers/nvme/host/core.c  |  1 +
 drivers/nvme/host/hwmon.c | 87 +++++++++++++++++++++++++++++++++++++++
 drivers/nvme/host/nvme.h  |  1 +
 include/linux/nvme.h      |  7 +++-
 4 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index b149b638453f..59608e65bc1c 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -3336,6 +3336,7 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)
 	ctrl->kas = le16_to_cpu(id->kas);
 	ctrl->max_namespaces = le32_to_cpu(id->mnan);
 	ctrl->ctratt = le32_to_cpu(id->ctratt);
+	ctrl->tmpthha = id->tmpthha;
 
 	ctrl->cntrltype = id->cntrltype;
 	ctrl->dctype = id->dctype;
diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c
index 89a1a1043d63..4f210787824e 100644
--- a/drivers/nvme/host/hwmon.c
+++ b/drivers/nvme/host/hwmon.c
@@ -37,6 +37,37 @@ static int nvme_get_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
 	return 0;
 }
 
+static int nvme_get_temp_thresh_hyst(struct nvme_ctrl *ctrl, bool under,
+				     long *hyst)
+{
+	unsigned int threshold = 0;
+	u32 status;
+	int ret;
+
+	if (under)
+		threshold |= NVME_TEMP_THRESH_TYPE_UNDER;
+
+	ret = nvme_get_features(ctrl, NVME_FEAT_TEMP_THRESH, threshold, NULL, 0,
+				&status);
+	if (ret > 0)
+		return -EIO;
+	if (ret < 0)
+		return ret;
+	*hyst =
+	    kelvin_to_millicelsius((status >> NVME_TEMP_THRESH_HYST_SHIFT) &
+				   NVME_TEMP_THRESH_HYST_MASK);
+	/*
+	 * Must be reported as an absolute temperature, NOT a delta from the max
+	 * or min value.
+	 */
+	if (under)
+		*hyst += kelvin_to_millicelsius(status & NVME_TEMP_THRESH_MASK);
+	else
+		*hyst -= kelvin_to_millicelsius(status & NVME_TEMP_THRESH_MASK);
+
+	return 0;
+}
+
 static int nvme_set_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
 				long temp)
 {
@@ -57,6 +88,48 @@ static int nvme_set_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
 	return ret;
 }
 
+static int nvme_set_temp_thresh_hyst(struct nvme_ctrl *ctrl, bool under,
+				     long hyst)
+{
+	unsigned int hysteresis = 0;
+	u32 status;
+	int ret;
+
+	if (under)
+		hysteresis |= NVME_TEMP_THRESH_TYPE_UNDER;
+
+	ret = nvme_get_features(ctrl, NVME_FEAT_TEMP_THRESH, hysteresis, NULL,
+				0, &status);
+	if (ret > 0)
+		return -EIO;
+	if (ret < 0)
+		return ret;
+
+	hyst = millicelsius_to_kelvin(hyst);
+	/*
+	 * Must be reported as an absolute temperature, NOT a delta from the max
+	 * or min value.
+	 */
+	if (under)
+		hyst -= status & NVME_TEMP_THRESH_MASK;
+	else
+		hyst = (status & NVME_TEMP_THRESH_MASK) - hyst;
+	hysteresis = clamp_val(hyst, 0, NVME_TEMP_THRESH_HYST_MASK);
+	hysteresis <<= NVME_TEMP_THRESH_HYST_SHIFT;
+
+	hysteresis |= status & NVME_TEMP_THRESH_MASK;
+
+	if (under)
+		hysteresis |= NVME_TEMP_THRESH_TYPE_UNDER;
+
+	ret = nvme_set_features(ctrl, NVME_FEAT_TEMP_THRESH, hysteresis, NULL,
+				0, NULL);
+	if (ret > 0)
+		return -EIO;
+
+	return ret;
+}
+
 static int nvme_hwmon_get_smart_log(struct nvme_hwmon_data *data)
 {
 	return nvme_get_log(data->ctrl, NVME_NSID_ALL, NVME_LOG_SMART, 0,
@@ -83,6 +156,10 @@ static int nvme_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 	case hwmon_temp_crit:
 		*val = kelvin_to_millicelsius(data->ctrl->cctemp);
 		return 0;
+	case hwmon_temp_max_hyst:
+		return nvme_get_temp_thresh_hyst(data->ctrl, false, val);
+	case hwmon_temp_min_hyst:
+		return nvme_get_temp_thresh_hyst(data->ctrl, true, val);
 	default:
 		break;
 	}
@@ -122,6 +199,10 @@ static int nvme_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 		return nvme_set_temp_thresh(data->ctrl, channel, false, val);
 	case hwmon_temp_min:
 		return nvme_set_temp_thresh(data->ctrl, channel, true, val);
+	case hwmon_temp_max_hyst:
+		return nvme_set_temp_thresh_hyst(data->ctrl, false, val);
+	case hwmon_temp_min_hyst:
+		return nvme_set_temp_thresh_hyst(data->ctrl, true, val);
 	default:
 		break;
 	}
@@ -160,6 +241,12 @@ static umode_t nvme_hwmon_is_visible(const void *_data,
 		if (!channel && data->ctrl->cctemp)
 			return 0444;
 		break;
+	case hwmon_temp_max_hyst:
+	case hwmon_temp_min_hyst:
+		if (channel ||
+		    !(data->ctrl->tmpthha & NVME_CTRL_TMPTHHA_TMPTHMH_MASK))
+			break;
+		fallthrough;
 	case hwmon_temp_max:
 	case hwmon_temp_min:
 		if ((!channel && data->ctrl->wctemp) ||
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 093cb423f536..4824946abca3 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -339,6 +339,7 @@ struct nvme_ctrl {
 	u32 oaes;
 	u32 aen_result;
 	u32 ctratt;
+	u8 tmpthha;
 	unsigned int shutdown_timeout;
 	unsigned int kato;
 	bool subsystem;
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index b58d9405d65e..d2bf61f7080a 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -332,7 +332,9 @@ struct nvme_id_ctrl {
 	__u8			anacap;
 	__le32			anagrpmax;
 	__le32			nanagrpid;
-	__u8			rsvd352[160];
+	__u8			rsvd352[32];
+	__u8			tmpthha;
+	__u8			rsvd385[127];
 	__u8			sqes;
 	__u8			cqes;
 	__le16			maxcmd;
@@ -388,6 +390,7 @@ enum {
 	NVME_CTRL_CTRATT_PREDICTABLE_LAT	= 1 << 5,
 	NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY	= 1 << 7,
 	NVME_CTRL_CTRATT_UUID_LIST		= 1 << 9,
+	NVME_CTRL_TMPTHHA_TMPTHMH_MASK		= 7,
 };
 
 struct nvme_lbaf {
@@ -1121,6 +1124,8 @@ enum {
 	NVME_TEMP_THRESH_MASK		= 0xffff,
 	NVME_TEMP_THRESH_SELECT_SHIFT	= 16,
 	NVME_TEMP_THRESH_TYPE_UNDER	= 0x100000,
+	NVME_TEMP_THRESH_HYST_SHIFT	= 22,
+	NVME_TEMP_THRESH_HYST_MASK	= 0x7,
 };
 
 struct nvme_feat_auto_pst {
-- 
2.45.2




More information about the Linux-nvme mailing list