[PATCH] nvme: hwmon: Add support for throttling temperature feature

Tokunori Ikegami ikegami.t at gmail.com
Fri Aug 5 22:46:06 PDT 2022


NVMe drives support host controlled thermal management feature as optional.
The thermal management temperature are different from the temperature threshold.
So add functionality to set the throttling temperature values.

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

diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 2e2cd79d89eb..be756ed8b71c 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -486,6 +486,8 @@ static const char * const hwmon_temp_attr_templates[] = {
 	[hwmon_temp_reset_history] = "temp%d_reset_history",
 	[hwmon_temp_rated_min] = "temp%d_rated_min",
 	[hwmon_temp_rated_max] = "temp%d_rated_max",
+	[hwmon_temp_throttle_low] = "temp%d_throttle_low",
+	[hwmon_temp_throttle_high] = "temp%d_throttle_high",
 };
 
 static const char * const hwmon_in_attr_templates[] = {
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 2429b11eb9a8..7925f8d3bedf 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -3142,6 +3142,7 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)
 	nvme_set_queue_limits(ctrl, ctrl->admin_q);
 	ctrl->sgls = le32_to_cpu(id->sgls);
 	ctrl->kas = le16_to_cpu(id->kas);
+	ctrl->hctma = le16_to_cpu(id->hctma);
 	ctrl->max_namespaces = le32_to_cpu(id->mnan);
 	ctrl->ctratt = le32_to_cpu(id->ctratt);
 
diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c
index 0a586d712920..396d6304fde1 100644
--- a/drivers/nvme/host/hwmon.c
+++ b/drivers/nvme/host/hwmon.c
@@ -57,6 +57,50 @@ static int nvme_set_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
 	return ret;
 }
 
+static int nvme_get_temp_throttle(struct nvme_ctrl *ctrl, int sensor, bool low,
+				  long *temp)
+{
+	struct nvme_feat_hctm hctm;
+	int ret;
+
+	ret = nvme_get_features(ctrl, NVME_FEAT_HCTM, 0, NULL, 0, (u32 *)&hctm);
+	if (ret > 0)
+		return -EIO;
+	if (ret < 0)
+		return ret;
+
+	*temp = kelvin_to_millicelsius(low ? hctm.tmt1 : hctm.tmt2);
+
+	return 0;
+}
+
+static int nvme_set_temp_throttle(struct nvme_ctrl *ctrl, int sensor, bool low,
+				  long temp)
+{
+	struct nvme_feat_hctm hctm;
+	int ret;
+
+	temp = millicelsius_to_kelvin(temp);
+
+	ret = nvme_get_features(ctrl, NVME_FEAT_HCTM, 0, NULL, 0, (u32 *)&hctm);
+	if (ret > 0)
+		return -EIO;
+	if (ret < 0)
+		return ret;
+
+	if (low)
+		hctm.tmt1 = temp;
+	else
+		hctm.tmt2 = temp;
+
+	ret = nvme_set_features(ctrl, NVME_FEAT_HCTM, *(unsigned int *)&hctm,
+				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 +127,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_throttle_high:
+		return nvme_get_temp_throttle(data->ctrl, channel, false, val);
+	case hwmon_temp_throttle_low:
+		return nvme_get_temp_throttle(data->ctrl, channel, true, val);
 	default:
 		break;
 	}
@@ -122,6 +170,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_throttle_high:
+		return nvme_set_temp_throttle(data->ctrl, channel, false, val);
+	case hwmon_temp_throttle_low:
+		return nvme_set_temp_throttle(data->ctrl, channel, true, val);
 	default:
 		break;
 	}
@@ -179,6 +231,11 @@ static umode_t nvme_hwmon_is_visible(const void *_data,
 		if (!channel || data->log.temp_sensor[channel - 1])
 			return 0444;
 		break;
+	case hwmon_temp_throttle_high:
+	case hwmon_temp_throttle_low:
+		if ((!channel && data->ctrl->hctma))
+			return 0644;
+		break;
 	default:
 		break;
 	}
@@ -189,7 +246,8 @@ static const struct hwmon_channel_info *nvme_hwmon_info[] = {
 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
 	HWMON_CHANNEL_INFO(temp,
 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
-				HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM,
+				HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM |
+				HWMON_T_THROTTLE_HIGH | HWMON_T_THROTTLE_LOW,
 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
 				HWMON_T_LABEL,
 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index bdc0ff7ed9ab..9f53f96c1206 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -293,6 +293,7 @@ struct nvme_ctrl {
 	u32 vs;
 	u32 sgls;
 	u16 kas;
+	u16 hctma;
 	u8 npss;
 	u8 apsta;
 	u16 wctemp;
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 14325f93c6b2..9377682c4b1a 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -87,6 +87,8 @@ enum hwmon_temp_attributes {
 	hwmon_temp_reset_history,
 	hwmon_temp_rated_min,
 	hwmon_temp_rated_max,
+	hwmon_temp_throttle_low,
+	hwmon_temp_throttle_high,
 };
 
 #define HWMON_T_ENABLE		BIT(hwmon_temp_enable)
@@ -116,6 +118,8 @@ enum hwmon_temp_attributes {
 #define HWMON_T_RESET_HISTORY	BIT(hwmon_temp_reset_history)
 #define HWMON_T_RATED_MIN	BIT(hwmon_temp_rated_min)
 #define HWMON_T_RATED_MAX	BIT(hwmon_temp_rated_max)
+#define HWMON_T_THROTTLE_LOW	BIT(hwmon_temp_throttle_low)
+#define HWMON_T_THROTTLE_HIGH	BIT(hwmon_temp_throttle_high)
 
 enum hwmon_in_attributes {
 	hwmon_in_enable,
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index ae53d74f3696..7f072fc2644d 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -184,7 +184,7 @@ enum {
  * Submission and Completion Queue Entry Sizes for the NVM command set.
  * (In bytes and specified as a power of two (2^n)).
  */
-#define NVME_ADM_SQES       6
+#define NVME_ADM_SQES		6
 #define NVME_NVM_IOSQES		6
 #define NVME_NVM_IOCQES		4
 
@@ -1079,6 +1079,11 @@ enum {
 	NVME_HOST_MEM_RETURN	= (1 << 1),
 };
 
+struct nvme_feat_hctm {
+	__u16 tmt2;
+	__u16 tmt1;
+};
+
 struct nvme_feat_host_behavior {
 	__u8 acre;
 	__u8 etdas;
-- 
2.34.1




More information about the Linux-nvme mailing list