Commit | Line | Data |
---|---|---|
55c5e0f8 | 1 | /**************************************************************************** |
f7a6d2c4 BH |
2 | * Driver for Solarflare network controllers and boards |
3 | * Copyright 2011-2013 Solarflare Communications Inc. | |
55c5e0f8 BH |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation, incorporated herein by reference. | |
8 | */ | |
9 | ||
10 | #include <linux/bitops.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/hwmon.h> | |
13 | #include <linux/stat.h> | |
14 | ||
15 | #include "net_driver.h" | |
16 | #include "mcdi.h" | |
17 | #include "mcdi_pcol.h" | |
18 | #include "nic.h" | |
19 | ||
20 | enum efx_hwmon_type { | |
21 | EFX_HWMON_UNKNOWN, | |
22 | EFX_HWMON_TEMP, /* temperature */ | |
23 | EFX_HWMON_COOL, /* cooling device, probably a heatsink */ | |
38589cdc BH |
24 | EFX_HWMON_IN, /* voltage */ |
25 | EFX_HWMON_CURR, /* current */ | |
26 | EFX_HWMON_POWER, /* power */ | |
55c5e0f8 BH |
27 | }; |
28 | ||
29 | static const struct { | |
30 | const char *label; | |
31 | enum efx_hwmon_type hwmon_type; | |
32 | int port; | |
d4fbdcfe | 33 | } efx_mcdi_sensor_type[] = { |
38589cdc BH |
34 | #define SENSOR(name, label, hwmon_type, port) \ |
35 | [MC_CMD_SENSOR_##name] = { label, EFX_HWMON_ ## hwmon_type, port } | |
36 | SENSOR(CONTROLLER_TEMP, "Controller ext. temp.", TEMP, -1), | |
37 | SENSOR(PHY_COMMON_TEMP, "PHY temp.", TEMP, -1), | |
38 | SENSOR(CONTROLLER_COOLING, "Controller cooling", COOL, -1), | |
39 | SENSOR(PHY0_TEMP, "PHY temp.", TEMP, 0), | |
40 | SENSOR(PHY0_COOLING, "PHY cooling", COOL, 0), | |
41 | SENSOR(PHY1_TEMP, "PHY temp.", TEMP, 1), | |
42 | SENSOR(PHY1_COOLING, "PHY cooling", COOL, 1), | |
43 | SENSOR(IN_1V0, "1.0V supply", IN, -1), | |
44 | SENSOR(IN_1V2, "1.2V supply", IN, -1), | |
45 | SENSOR(IN_1V8, "1.8V supply", IN, -1), | |
46 | SENSOR(IN_2V5, "2.5V supply", IN, -1), | |
47 | SENSOR(IN_3V3, "3.3V supply", IN, -1), | |
48 | SENSOR(IN_12V0, "12.0V supply", IN, -1), | |
49 | SENSOR(IN_1V2A, "1.2V analogue supply", IN, -1), | |
50 | SENSOR(IN_VREF, "ref. voltage", IN, -1), | |
51 | SENSOR(OUT_VAOE, "AOE power supply", IN, -1), | |
52 | SENSOR(AOE_TEMP, "AOE temp.", TEMP, -1), | |
53 | SENSOR(PSU_AOE_TEMP, "AOE PSU temp.", TEMP, -1), | |
54 | SENSOR(PSU_TEMP, "Controller PSU temp.", TEMP, -1), | |
55 | SENSOR(FAN_0, NULL, COOL, -1), | |
56 | SENSOR(FAN_1, NULL, COOL, -1), | |
57 | SENSOR(FAN_2, NULL, COOL, -1), | |
58 | SENSOR(FAN_3, NULL, COOL, -1), | |
59 | SENSOR(FAN_4, NULL, COOL, -1), | |
60 | SENSOR(IN_VAOE, "AOE input supply", IN, -1), | |
61 | SENSOR(OUT_IAOE, "AOE output current", CURR, -1), | |
62 | SENSOR(IN_IAOE, "AOE input current", CURR, -1), | |
63 | SENSOR(NIC_POWER, "Board power use", POWER, -1), | |
64 | SENSOR(IN_0V9, "0.9V supply", IN, -1), | |
65 | SENSOR(IN_I0V9, "0.9V input current", CURR, -1), | |
66 | SENSOR(IN_I1V2, "1.2V input current", CURR, -1), | |
67 | SENSOR(IN_0V9_ADC, "0.9V supply (at ADC)", IN, -1), | |
68 | SENSOR(CONTROLLER_2_TEMP, "Controller ext. temp. 2", TEMP, -1), | |
69 | SENSOR(VREG_INTERNAL_TEMP, "Voltage regulator temp.", TEMP, -1), | |
70 | SENSOR(VREG_0V9_TEMP, "0.9V regulator temp.", TEMP, -1), | |
71 | SENSOR(VREG_1V2_TEMP, "1.2V regulator temp.", TEMP, -1), | |
72 | SENSOR(CONTROLLER_VPTAT, "Controller int. temp. raw", IN, -1), | |
73 | SENSOR(CONTROLLER_INTERNAL_TEMP, "Controller int. temp.", TEMP, -1), | |
74 | SENSOR(CONTROLLER_VPTAT_EXTADC, | |
75 | "Controller int. temp. raw (at ADC)", IN, -1), | |
76 | SENSOR(CONTROLLER_INTERNAL_TEMP_EXTADC, | |
77 | "Controller int. temp. (via ADC)", TEMP, -1), | |
78 | SENSOR(AMBIENT_TEMP, "Ambient temp.", TEMP, -1), | |
79 | SENSOR(AIRFLOW, "Air flow raw", IN, -1), | |
55c5e0f8 BH |
80 | #undef SENSOR |
81 | }; | |
82 | ||
83 | static const char *const sensor_status_names[] = { | |
84 | [MC_CMD_SENSOR_STATE_OK] = "OK", | |
85 | [MC_CMD_SENSOR_STATE_WARNING] = "Warning", | |
86 | [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", | |
87 | [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", | |
8c4e720f | 88 | [MC_CMD_SENSOR_STATE_NO_READING] = "No reading", |
55c5e0f8 BH |
89 | }; |
90 | ||
91 | void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) | |
92 | { | |
93 | unsigned int type, state, value; | |
94 | const char *name = NULL, *state_txt; | |
95 | ||
96 | type = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); | |
97 | state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); | |
98 | value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); | |
99 | ||
100 | /* Deal gracefully with the board having more drivers than we | |
101 | * know about, but do not expect new sensor states. */ | |
102 | if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) | |
103 | name = efx_mcdi_sensor_type[type].label; | |
104 | if (!name) | |
105 | name = "No sensor name available"; | |
106 | EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); | |
107 | state_txt = sensor_status_names[state]; | |
108 | ||
109 | netif_err(efx, hw, efx->net_dev, | |
110 | "Sensor %d (%s) reports condition '%s' for raw value %d\n", | |
111 | type, name, state_txt, value); | |
112 | } | |
113 | ||
114 | #ifdef CONFIG_SFC_MCDI_MON | |
115 | ||
116 | struct efx_mcdi_mon_attribute { | |
117 | struct device_attribute dev_attr; | |
118 | unsigned int index; | |
119 | unsigned int type; | |
d4fbdcfe | 120 | enum efx_hwmon_type hwmon_type; |
55c5e0f8 BH |
121 | unsigned int limit_value; |
122 | char name[12]; | |
123 | }; | |
124 | ||
125 | static int efx_mcdi_mon_update(struct efx_nic *efx) | |
126 | { | |
127 | struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); | |
d4fbdcfe | 128 | MCDI_DECLARE_BUF(inbuf, MC_CMD_READ_SENSORS_EXT_IN_LEN); |
55c5e0f8 BH |
129 | int rc; |
130 | ||
d4fbdcfe | 131 | MCDI_SET_QWORD(inbuf, READ_SENSORS_EXT_IN_DMA_ADDR, |
338f74df | 132 | hwmon->dma_buf.dma_addr); |
d4fbdcfe | 133 | MCDI_SET_DWORD(inbuf, READ_SENSORS_EXT_IN_LENGTH, hwmon->dma_buf.len); |
55c5e0f8 BH |
134 | |
135 | rc = efx_mcdi_rpc(efx, MC_CMD_READ_SENSORS, | |
136 | inbuf, sizeof(inbuf), NULL, 0, NULL); | |
137 | if (rc == 0) | |
138 | hwmon->last_update = jiffies; | |
139 | return rc; | |
140 | } | |
141 | ||
55c5e0f8 BH |
142 | static int efx_mcdi_mon_get_entry(struct device *dev, unsigned int index, |
143 | efx_dword_t *entry) | |
144 | { | |
85493e6d | 145 | struct efx_nic *efx = dev_get_drvdata(dev->parent); |
55c5e0f8 BH |
146 | struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); |
147 | int rc; | |
148 | ||
149 | BUILD_BUG_ON(MC_CMD_READ_SENSORS_OUT_LEN != 0); | |
150 | ||
151 | mutex_lock(&hwmon->update_lock); | |
152 | ||
153 | /* Use cached value if last update was < 1 s ago */ | |
154 | if (time_before(jiffies, hwmon->last_update + HZ)) | |
155 | rc = 0; | |
156 | else | |
157 | rc = efx_mcdi_mon_update(efx); | |
158 | ||
159 | /* Copy out the requested entry */ | |
160 | *entry = ((efx_dword_t *)hwmon->dma_buf.addr)[index]; | |
161 | ||
162 | mutex_unlock(&hwmon->update_lock); | |
163 | ||
164 | return rc; | |
165 | } | |
166 | ||
167 | static ssize_t efx_mcdi_mon_show_value(struct device *dev, | |
168 | struct device_attribute *attr, | |
169 | char *buf) | |
170 | { | |
171 | struct efx_mcdi_mon_attribute *mon_attr = | |
172 | container_of(attr, struct efx_mcdi_mon_attribute, dev_attr); | |
173 | efx_dword_t entry; | |
8c4e720f | 174 | unsigned int value, state; |
55c5e0f8 BH |
175 | int rc; |
176 | ||
177 | rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry); | |
178 | if (rc) | |
179 | return rc; | |
180 | ||
8c4e720f AR |
181 | state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE); |
182 | if (state == MC_CMD_SENSOR_STATE_NO_READING) | |
183 | return -EBUSY; | |
184 | ||
55c5e0f8 BH |
185 | value = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_VALUE); |
186 | ||
38589cdc BH |
187 | switch (mon_attr->hwmon_type) { |
188 | case EFX_HWMON_TEMP: | |
189 | /* Convert temperature from degrees to milli-degrees Celsius */ | |
55c5e0f8 | 190 | value *= 1000; |
38589cdc BH |
191 | break; |
192 | case EFX_HWMON_POWER: | |
193 | /* Convert power from watts to microwatts */ | |
194 | value *= 1000000; | |
195 | break; | |
196 | default: | |
197 | /* No conversion needed */ | |
198 | break; | |
199 | } | |
55c5e0f8 BH |
200 | |
201 | return sprintf(buf, "%u\n", value); | |
202 | } | |
203 | ||
204 | static ssize_t efx_mcdi_mon_show_limit(struct device *dev, | |
205 | struct device_attribute *attr, | |
206 | char *buf) | |
207 | { | |
208 | struct efx_mcdi_mon_attribute *mon_attr = | |
209 | container_of(attr, struct efx_mcdi_mon_attribute, dev_attr); | |
210 | unsigned int value; | |
211 | ||
212 | value = mon_attr->limit_value; | |
213 | ||
38589cdc BH |
214 | switch (mon_attr->hwmon_type) { |
215 | case EFX_HWMON_TEMP: | |
216 | /* Convert temperature from degrees to milli-degrees Celsius */ | |
55c5e0f8 | 217 | value *= 1000; |
38589cdc BH |
218 | break; |
219 | case EFX_HWMON_POWER: | |
220 | /* Convert power from watts to microwatts */ | |
221 | value *= 1000000; | |
222 | break; | |
223 | default: | |
224 | /* No conversion needed */ | |
225 | break; | |
226 | } | |
55c5e0f8 BH |
227 | |
228 | return sprintf(buf, "%u\n", value); | |
229 | } | |
230 | ||
231 | static ssize_t efx_mcdi_mon_show_alarm(struct device *dev, | |
232 | struct device_attribute *attr, | |
233 | char *buf) | |
234 | { | |
235 | struct efx_mcdi_mon_attribute *mon_attr = | |
236 | container_of(attr, struct efx_mcdi_mon_attribute, dev_attr); | |
237 | efx_dword_t entry; | |
238 | int state; | |
239 | int rc; | |
240 | ||
241 | rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry); | |
242 | if (rc) | |
243 | return rc; | |
244 | ||
245 | state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE); | |
246 | return sprintf(buf, "%d\n", state != MC_CMD_SENSOR_STATE_OK); | |
247 | } | |
248 | ||
249 | static ssize_t efx_mcdi_mon_show_label(struct device *dev, | |
250 | struct device_attribute *attr, | |
251 | char *buf) | |
252 | { | |
253 | struct efx_mcdi_mon_attribute *mon_attr = | |
254 | container_of(attr, struct efx_mcdi_mon_attribute, dev_attr); | |
255 | return sprintf(buf, "%s\n", | |
256 | efx_mcdi_sensor_type[mon_attr->type].label); | |
257 | } | |
258 | ||
85493e6d | 259 | static void |
55c5e0f8 BH |
260 | efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name, |
261 | ssize_t (*reader)(struct device *, | |
262 | struct device_attribute *, char *), | |
263 | unsigned int index, unsigned int type, | |
264 | unsigned int limit_value) | |
265 | { | |
266 | struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); | |
267 | struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs]; | |
55c5e0f8 BH |
268 | |
269 | strlcpy(attr->name, name, sizeof(attr->name)); | |
270 | attr->index = index; | |
271 | attr->type = type; | |
d4fbdcfe BH |
272 | if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) |
273 | attr->hwmon_type = efx_mcdi_sensor_type[type].hwmon_type; | |
274 | else | |
275 | attr->hwmon_type = EFX_HWMON_UNKNOWN; | |
55c5e0f8 | 276 | attr->limit_value = limit_value; |
a9ec6bd1 | 277 | sysfs_attr_init(&attr->dev_attr.attr); |
55c5e0f8 BH |
278 | attr->dev_attr.attr.name = attr->name; |
279 | attr->dev_attr.attr.mode = S_IRUGO; | |
280 | attr->dev_attr.show = reader; | |
85493e6d | 281 | hwmon->group.attrs[hwmon->n_attrs++] = &attr->dev_attr.attr; |
55c5e0f8 BH |
282 | } |
283 | ||
284 | int efx_mcdi_mon_probe(struct efx_nic *efx) | |
285 | { | |
38589cdc | 286 | unsigned int n_temp = 0, n_cool = 0, n_in = 0, n_curr = 0, n_power = 0; |
55c5e0f8 | 287 | struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); |
d4fbdcfe | 288 | MCDI_DECLARE_BUF(inbuf, MC_CMD_SENSOR_INFO_EXT_IN_LEN); |
59cfc479 | 289 | MCDI_DECLARE_BUF(outbuf, MC_CMD_SENSOR_INFO_OUT_LENMAX); |
d4fbdcfe | 290 | unsigned int n_pages, n_sensors, n_attrs, page; |
55c5e0f8 BH |
291 | size_t outlen; |
292 | char name[12]; | |
293 | u32 mask; | |
d4fbdcfe | 294 | int rc, i, j, type; |
55c5e0f8 | 295 | |
d4fbdcfe BH |
296 | /* Find out how many sensors are present */ |
297 | n_sensors = 0; | |
298 | page = 0; | |
299 | do { | |
300 | MCDI_SET_DWORD(inbuf, SENSOR_INFO_EXT_IN_PAGE, page); | |
55c5e0f8 | 301 | |
d4fbdcfe BH |
302 | rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO, inbuf, sizeof(inbuf), |
303 | outbuf, sizeof(outbuf), &outlen); | |
304 | if (rc) | |
305 | return rc; | |
306 | if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN) | |
307 | return -EIO; | |
308 | ||
309 | mask = MCDI_DWORD(outbuf, SENSOR_INFO_OUT_MASK); | |
310 | n_sensors += hweight32(mask & ~(1 << MC_CMD_SENSOR_PAGE0_NEXT)); | |
311 | ++page; | |
312 | } while (mask & (1 << MC_CMD_SENSOR_PAGE0_NEXT)); | |
313 | n_pages = page; | |
314 | ||
315 | /* Don't create a device if there are none */ | |
316 | if (n_sensors == 0) | |
55c5e0f8 BH |
317 | return 0; |
318 | ||
d4fbdcfe BH |
319 | rc = efx_nic_alloc_buffer( |
320 | efx, &hwmon->dma_buf, | |
321 | n_sensors * MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_LEN, | |
322 | GFP_KERNEL); | |
55c5e0f8 BH |
323 | if (rc) |
324 | return rc; | |
325 | ||
326 | mutex_init(&hwmon->update_lock); | |
327 | efx_mcdi_mon_update(efx); | |
328 | ||
329 | /* Allocate space for the maximum possible number of | |
85493e6d | 330 | * attributes for this set of sensors: |
55c5e0f8 BH |
331 | * value, min, max, crit, alarm and label for each sensor. |
332 | */ | |
85493e6d | 333 | n_attrs = 6 * n_sensors; |
55c5e0f8 BH |
334 | hwmon->attrs = kcalloc(n_attrs, sizeof(*hwmon->attrs), GFP_KERNEL); |
335 | if (!hwmon->attrs) { | |
336 | rc = -ENOMEM; | |
337 | goto fail; | |
338 | } | |
85493e6d GR |
339 | hwmon->group.attrs = kcalloc(n_attrs + 1, sizeof(struct attribute *), |
340 | GFP_KERNEL); | |
341 | if (!hwmon->group.attrs) { | |
342 | rc = -ENOMEM; | |
55c5e0f8 BH |
343 | goto fail; |
344 | } | |
345 | ||
d4fbdcfe BH |
346 | for (i = 0, j = -1, type = -1; ; i++) { |
347 | enum efx_hwmon_type hwmon_type; | |
55c5e0f8 BH |
348 | const char *hwmon_prefix; |
349 | unsigned hwmon_index; | |
350 | u16 min1, max1, min2, max2; | |
351 | ||
352 | /* Find next sensor type or exit if there is none */ | |
d4fbdcfe | 353 | do { |
55c5e0f8 | 354 | type++; |
55c5e0f8 | 355 | |
d4fbdcfe BH |
356 | if ((type % 32) == 0) { |
357 | page = type / 32; | |
358 | j = -1; | |
359 | if (page == n_pages) | |
85493e6d | 360 | goto hwmon_register; |
d4fbdcfe BH |
361 | |
362 | MCDI_SET_DWORD(inbuf, SENSOR_INFO_EXT_IN_PAGE, | |
363 | page); | |
364 | rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO, | |
365 | inbuf, sizeof(inbuf), | |
366 | outbuf, sizeof(outbuf), | |
367 | &outlen); | |
368 | if (rc) | |
369 | goto fail; | |
370 | if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN) { | |
371 | rc = -EIO; | |
372 | goto fail; | |
373 | } | |
374 | ||
375 | mask = (MCDI_DWORD(outbuf, | |
376 | SENSOR_INFO_OUT_MASK) & | |
377 | ~(1 << MC_CMD_SENSOR_PAGE0_NEXT)); | |
378 | ||
379 | /* Check again for short response */ | |
380 | if (outlen < | |
381 | MC_CMD_SENSOR_INFO_OUT_LEN(hweight32(mask))) { | |
382 | rc = -EIO; | |
383 | goto fail; | |
384 | } | |
385 | } | |
386 | } while (!(mask & (1 << type % 32))); | |
387 | j++; | |
388 | ||
389 | if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) { | |
390 | hwmon_type = efx_mcdi_sensor_type[type].hwmon_type; | |
391 | ||
392 | /* Skip sensors specific to a different port */ | |
393 | if (hwmon_type != EFX_HWMON_UNKNOWN && | |
394 | efx_mcdi_sensor_type[type].port >= 0 && | |
395 | efx_mcdi_sensor_type[type].port != | |
396 | efx_port_num(efx)) | |
397 | continue; | |
398 | } else { | |
399 | hwmon_type = EFX_HWMON_UNKNOWN; | |
400 | } | |
55c5e0f8 | 401 | |
d4fbdcfe | 402 | switch (hwmon_type) { |
55c5e0f8 BH |
403 | case EFX_HWMON_TEMP: |
404 | hwmon_prefix = "temp"; | |
405 | hwmon_index = ++n_temp; /* 1-based */ | |
406 | break; | |
407 | case EFX_HWMON_COOL: | |
408 | /* This is likely to be a heatsink, but there | |
409 | * is no convention for representing cooling | |
410 | * devices other than fans. | |
411 | */ | |
412 | hwmon_prefix = "fan"; | |
413 | hwmon_index = ++n_cool; /* 1-based */ | |
414 | break; | |
415 | default: | |
416 | hwmon_prefix = "in"; | |
417 | hwmon_index = n_in++; /* 0-based */ | |
418 | break; | |
38589cdc BH |
419 | case EFX_HWMON_CURR: |
420 | hwmon_prefix = "curr"; | |
421 | hwmon_index = ++n_curr; /* 1-based */ | |
422 | break; | |
423 | case EFX_HWMON_POWER: | |
424 | hwmon_prefix = "power"; | |
425 | hwmon_index = ++n_power; /* 1-based */ | |
426 | break; | |
55c5e0f8 BH |
427 | } |
428 | ||
429 | min1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY, | |
d4fbdcfe | 430 | SENSOR_INFO_ENTRY, j, MIN1); |
55c5e0f8 | 431 | max1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY, |
d4fbdcfe | 432 | SENSOR_INFO_ENTRY, j, MAX1); |
55c5e0f8 | 433 | min2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY, |
d4fbdcfe | 434 | SENSOR_INFO_ENTRY, j, MIN2); |
55c5e0f8 | 435 | max2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY, |
d4fbdcfe | 436 | SENSOR_INFO_ENTRY, j, MAX2); |
55c5e0f8 BH |
437 | |
438 | if (min1 != max1) { | |
439 | snprintf(name, sizeof(name), "%s%u_input", | |
440 | hwmon_prefix, hwmon_index); | |
85493e6d | 441 | efx_mcdi_mon_add_attr( |
55c5e0f8 | 442 | efx, name, efx_mcdi_mon_show_value, i, type, 0); |
55c5e0f8 | 443 | |
38589cdc BH |
444 | if (hwmon_type != EFX_HWMON_POWER) { |
445 | snprintf(name, sizeof(name), "%s%u_min", | |
446 | hwmon_prefix, hwmon_index); | |
85493e6d | 447 | efx_mcdi_mon_add_attr( |
38589cdc BH |
448 | efx, name, efx_mcdi_mon_show_limit, |
449 | i, type, min1); | |
38589cdc | 450 | } |
55c5e0f8 BH |
451 | |
452 | snprintf(name, sizeof(name), "%s%u_max", | |
453 | hwmon_prefix, hwmon_index); | |
85493e6d | 454 | efx_mcdi_mon_add_attr( |
55c5e0f8 BH |
455 | efx, name, efx_mcdi_mon_show_limit, |
456 | i, type, max1); | |
55c5e0f8 BH |
457 | |
458 | if (min2 != max2) { | |
459 | /* Assume max2 is critical value. | |
460 | * But we have no good way to expose min2. | |
461 | */ | |
462 | snprintf(name, sizeof(name), "%s%u_crit", | |
463 | hwmon_prefix, hwmon_index); | |
85493e6d | 464 | efx_mcdi_mon_add_attr( |
55c5e0f8 BH |
465 | efx, name, efx_mcdi_mon_show_limit, |
466 | i, type, max2); | |
55c5e0f8 BH |
467 | } |
468 | } | |
469 | ||
470 | snprintf(name, sizeof(name), "%s%u_alarm", | |
471 | hwmon_prefix, hwmon_index); | |
85493e6d | 472 | efx_mcdi_mon_add_attr( |
55c5e0f8 | 473 | efx, name, efx_mcdi_mon_show_alarm, i, type, 0); |
55c5e0f8 | 474 | |
d4fbdcfe BH |
475 | if (type < ARRAY_SIZE(efx_mcdi_sensor_type) && |
476 | efx_mcdi_sensor_type[type].label) { | |
55c5e0f8 BH |
477 | snprintf(name, sizeof(name), "%s%u_label", |
478 | hwmon_prefix, hwmon_index); | |
85493e6d | 479 | efx_mcdi_mon_add_attr( |
55c5e0f8 | 480 | efx, name, efx_mcdi_mon_show_label, i, type, 0); |
55c5e0f8 BH |
481 | } |
482 | } | |
483 | ||
85493e6d GR |
484 | hwmon_register: |
485 | hwmon->groups[0] = &hwmon->group; | |
486 | hwmon->device = hwmon_device_register_with_groups(&efx->pci_dev->dev, | |
487 | KBUILD_MODNAME, NULL, | |
488 | hwmon->groups); | |
489 | if (IS_ERR(hwmon->device)) { | |
490 | rc = PTR_ERR(hwmon->device); | |
491 | goto fail; | |
492 | } | |
493 | ||
494 | return 0; | |
495 | ||
55c5e0f8 BH |
496 | fail: |
497 | efx_mcdi_mon_remove(efx); | |
498 | return rc; | |
499 | } | |
500 | ||
501 | void efx_mcdi_mon_remove(struct efx_nic *efx) | |
502 | { | |
e847b53e | 503 | struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); |
55c5e0f8 | 504 | |
55c5e0f8 BH |
505 | if (hwmon->device) |
506 | hwmon_device_unregister(hwmon->device); | |
85493e6d GR |
507 | kfree(hwmon->attrs); |
508 | kfree(hwmon->group.attrs); | |
55c5e0f8 BH |
509 | efx_nic_free_buffer(efx, &hwmon->dma_buf); |
510 | } | |
511 | ||
512 | #endif /* CONFIG_SFC_MCDI_MON */ |