1 /* Broadcom NetXtreme-C/E network driver.
2  *
3  * Copyright (c) 2023 Broadcom Limited
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation.
8  */
9 
10 #include <linux/dev_printk.h>
11 #include <linux/errno.h>
12 #include <linux/hwmon.h>
13 #include <linux/hwmon-sysfs.h>
14 #include <linux/pci.h>
15 
16 #include "bnxt_hsi.h"
17 #include "bnxt.h"
18 #include "bnxt_hwrm.h"
19 #include "bnxt_hwmon.h"
20 
21 void bnxt_hwmon_notify_event(struct bnxt *bp)
22 {
23 	u32 attr;
24 
25 	if (!bp->hwmon_dev)
26 		return;
27 
28 	switch (bp->thermal_threshold_type) {
29 	case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_WARN:
30 		attr = hwmon_temp_max_alarm;
31 		break;
32 	case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_CRITICAL:
33 		attr = hwmon_temp_crit_alarm;
34 		break;
35 	case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_FATAL:
36 	case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_SHUTDOWN:
37 		attr = hwmon_temp_emergency_alarm;
38 		break;
39 	default:
40 		return;
41 	}
42 
43 	hwmon_notify_event(&bp->pdev->dev, hwmon_temp, attr, 0);
44 }
45 
46 static int bnxt_hwrm_temp_query(struct bnxt *bp, u8 *temp)
47 {
48 	struct hwrm_temp_monitor_query_output *resp;
49 	struct hwrm_temp_monitor_query_input *req;
50 	int rc;
51 
52 	rc = hwrm_req_init(bp, req, HWRM_TEMP_MONITOR_QUERY);
53 	if (rc)
54 		return rc;
55 	resp = hwrm_req_hold(bp, req);
56 	rc = hwrm_req_send_silent(bp, req);
57 	if (rc)
58 		goto drop_req;
59 
60 	if (temp) {
61 		*temp = resp->temp;
62 	} else if (resp->flags &
63 		   TEMP_MONITOR_QUERY_RESP_FLAGS_THRESHOLD_VALUES_AVAILABLE) {
64 		bp->fw_cap |= BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED;
65 		bp->warn_thresh_temp = resp->warn_threshold;
66 		bp->crit_thresh_temp = resp->critical_threshold;
67 		bp->fatal_thresh_temp = resp->fatal_threshold;
68 		bp->shutdown_thresh_temp = resp->shutdown_threshold;
69 	}
70 drop_req:
71 	hwrm_req_drop(bp, req);
72 	return rc;
73 }
74 
75 static umode_t bnxt_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type,
76 				     u32 attr, int channel)
77 {
78 	const struct bnxt *bp = _data;
79 
80 	if (type != hwmon_temp)
81 		return 0;
82 
83 	switch (attr) {
84 	case hwmon_temp_input:
85 		return 0444;
86 	case hwmon_temp_max:
87 	case hwmon_temp_crit:
88 	case hwmon_temp_emergency:
89 	case hwmon_temp_max_alarm:
90 	case hwmon_temp_crit_alarm:
91 	case hwmon_temp_emergency_alarm:
92 		if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED))
93 			return 0;
94 		return 0444;
95 	default:
96 		return 0;
97 	}
98 }
99 
100 static int bnxt_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
101 			   int channel, long *val)
102 {
103 	struct bnxt *bp = dev_get_drvdata(dev);
104 	u8 temp = 0;
105 	int rc;
106 
107 	switch (attr) {
108 	case hwmon_temp_input:
109 		rc = bnxt_hwrm_temp_query(bp, &temp);
110 		if (!rc)
111 			*val = temp * 1000;
112 		return rc;
113 	case hwmon_temp_max:
114 		*val = bp->warn_thresh_temp * 1000;
115 		return 0;
116 	case hwmon_temp_crit:
117 		*val = bp->crit_thresh_temp * 1000;
118 		return 0;
119 	case hwmon_temp_emergency:
120 		*val = bp->fatal_thresh_temp * 1000;
121 		return 0;
122 	case hwmon_temp_max_alarm:
123 		rc = bnxt_hwrm_temp_query(bp, &temp);
124 		if (!rc)
125 			*val = temp >= bp->warn_thresh_temp;
126 		return rc;
127 	case hwmon_temp_crit_alarm:
128 		rc = bnxt_hwrm_temp_query(bp, &temp);
129 		if (!rc)
130 			*val = temp >= bp->crit_thresh_temp;
131 		return rc;
132 	case hwmon_temp_emergency_alarm:
133 		rc = bnxt_hwrm_temp_query(bp, &temp);
134 		if (!rc)
135 			*val = temp >= bp->fatal_thresh_temp;
136 		return rc;
137 	default:
138 		return -EOPNOTSUPP;
139 	}
140 }
141 
142 static const struct hwmon_channel_info *bnxt_hwmon_info[] = {
143 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
144 			   HWMON_T_EMERGENCY | HWMON_T_MAX_ALARM |
145 			   HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM),
146 	NULL
147 };
148 
149 static const struct hwmon_ops bnxt_hwmon_ops = {
150 	.is_visible     = bnxt_hwmon_is_visible,
151 	.read           = bnxt_hwmon_read,
152 };
153 
154 static const struct hwmon_chip_info bnxt_hwmon_chip_info = {
155 	.ops    = &bnxt_hwmon_ops,
156 	.info   = bnxt_hwmon_info,
157 };
158 
159 static ssize_t temp1_shutdown_show(struct device *dev,
160 				   struct device_attribute *attr, char *buf)
161 {
162 	struct bnxt *bp = dev_get_drvdata(dev);
163 
164 	return sysfs_emit(buf, "%u\n", bp->shutdown_thresh_temp * 1000);
165 }
166 
167 static ssize_t temp1_shutdown_alarm_show(struct device *dev,
168 					 struct device_attribute *attr, char *buf)
169 {
170 	struct bnxt *bp = dev_get_drvdata(dev);
171 	u8 temp;
172 	int rc;
173 
174 	rc = bnxt_hwrm_temp_query(bp, &temp);
175 	if (rc)
176 		return -EIO;
177 
178 	return sysfs_emit(buf, "%u\n", temp >= bp->shutdown_thresh_temp);
179 }
180 
181 static DEVICE_ATTR_RO(temp1_shutdown);
182 static DEVICE_ATTR_RO(temp1_shutdown_alarm);
183 
184 static struct attribute *bnxt_temp_extra_attrs[] = {
185 	&dev_attr_temp1_shutdown.attr,
186 	&dev_attr_temp1_shutdown_alarm.attr,
187 	NULL,
188 };
189 
190 static umode_t bnxt_temp_extra_attrs_visible(struct kobject *kobj,
191 					     struct attribute *attr, int index)
192 {
193 	struct device *dev = kobj_to_dev(kobj);
194 	struct bnxt *bp = dev_get_drvdata(dev);
195 
196 	/* Shutdown temperature setting in NVM is optional */
197 	if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED) ||
198 	    !bp->shutdown_thresh_temp)
199 		return 0;
200 
201 	return attr->mode;
202 }
203 
204 static const struct attribute_group bnxt_temp_extra_group = {
205 	.attrs		= bnxt_temp_extra_attrs,
206 	.is_visible	= bnxt_temp_extra_attrs_visible,
207 };
208 __ATTRIBUTE_GROUPS(bnxt_temp_extra);
209 
210 void bnxt_hwmon_uninit(struct bnxt *bp)
211 {
212 	if (bp->hwmon_dev) {
213 		hwmon_device_unregister(bp->hwmon_dev);
214 		bp->hwmon_dev = NULL;
215 	}
216 }
217 
218 void bnxt_hwmon_init(struct bnxt *bp)
219 {
220 	struct pci_dev *pdev = bp->pdev;
221 	int rc;
222 
223 	/* temp1_xxx is only sensor, ensure not registered if it will fail */
224 	rc = bnxt_hwrm_temp_query(bp, NULL);
225 	if (rc == -EACCES || rc == -EOPNOTSUPP) {
226 		bnxt_hwmon_uninit(bp);
227 		return;
228 	}
229 
230 	if (bp->hwmon_dev)
231 		return;
232 
233 	bp->hwmon_dev = hwmon_device_register_with_info(&pdev->dev,
234 							DRV_MODULE_NAME, bp,
235 							&bnxt_hwmon_chip_info,
236 							bnxt_temp_extra_groups);
237 	if (IS_ERR(bp->hwmon_dev)) {
238 		bp->hwmon_dev = NULL;
239 		dev_warn(&pdev->dev, "Cannot register hwmon device\n");
240 	}
241 }
242