[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