aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc/mcdi_mon.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2012-01-06 15:25:39 -0500
committerBen Hutchings <bhutchings@solarflare.com>2012-01-26 19:10:53 -0500
commit55c5e0f85dc550f03dc8a0b0097da6af3b4865c5 (patch)
treeb6f2087958e641be791136e79912f69656391225 /drivers/net/ethernet/sfc/mcdi_mon.c
parent1646a6f352a6f70fcca828589ed04797aa09d494 (diff)
sfc: Add hwmon driver for boards using SFC9000-family controllers
The SFC9000-family controllers have firmware to manage all board peripherals including temperature, heat sink continuity and voltage sensors. The firmware reports sensor alarms, which we log, and will shut down the board if necessary. Some users may want to monitor their boards more closely, so add an hwmon driver that exposes all sensors reported by the firmware. Move efx_mcdi_sensor_event() into the new file so it can share the array of sensor labels with the hwmon driver. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/ethernet/sfc/mcdi_mon.c')
-rw-r--r--drivers/net/ethernet/sfc/mcdi_mon.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/drivers/net/ethernet/sfc/mcdi_mon.c b/drivers/net/ethernet/sfc/mcdi_mon.c
new file mode 100644
index 000000000000..8a72c10b9a6c
--- /dev/null
+++ b/drivers/net/ethernet/sfc/mcdi_mon.c
@@ -0,0 +1,415 @@
1/****************************************************************************
2 * Driver for Solarflare Solarstorm network controllers and boards
3 * Copyright 2011 Solarflare Communications Inc.
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
20enum efx_hwmon_type {
21 EFX_HWMON_UNKNOWN,
22 EFX_HWMON_TEMP, /* temperature */
23 EFX_HWMON_COOL, /* cooling device, probably a heatsink */
24 EFX_HWMON_IN /* input voltage */
25};
26
27static const struct {
28 const char *label;
29 enum efx_hwmon_type hwmon_type;
30 int port;
31} efx_mcdi_sensor_type[MC_CMD_SENSOR_ENTRY_MAXNUM] = {
32#define SENSOR(name, label, hwmon_type, port) \
33 [MC_CMD_SENSOR_##name] = { label, hwmon_type, port }
34 SENSOR(CONTROLLER_TEMP, "Controller temp.", EFX_HWMON_TEMP, -1),
35 SENSOR(PHY_COMMON_TEMP, "PHY temp.", EFX_HWMON_TEMP, -1),
36 SENSOR(CONTROLLER_COOLING, "Controller cooling", EFX_HWMON_COOL, -1),
37 SENSOR(PHY0_TEMP, "PHY temp.", EFX_HWMON_TEMP, 0),
38 SENSOR(PHY0_COOLING, "PHY cooling", EFX_HWMON_COOL, 0),
39 SENSOR(PHY1_TEMP, "PHY temp.", EFX_HWMON_TEMP, 1),
40 SENSOR(PHY1_COOLING, "PHY cooling", EFX_HWMON_COOL, 1),
41 SENSOR(IN_1V0, "1.0V supply", EFX_HWMON_IN, -1),
42 SENSOR(IN_1V2, "1.2V supply", EFX_HWMON_IN, -1),
43 SENSOR(IN_1V8, "1.8V supply", EFX_HWMON_IN, -1),
44 SENSOR(IN_2V5, "2.5V supply", EFX_HWMON_IN, -1),
45 SENSOR(IN_3V3, "3.3V supply", EFX_HWMON_IN, -1),
46 SENSOR(IN_12V0, "12.0V supply", EFX_HWMON_IN, -1),
47 SENSOR(IN_1V2A, "1.2V analogue supply", EFX_HWMON_IN, -1),
48 SENSOR(IN_VREF, "ref. voltage", EFX_HWMON_IN, -1),
49#undef SENSOR
50};
51
52static const char *const sensor_status_names[] = {
53 [MC_CMD_SENSOR_STATE_OK] = "OK",
54 [MC_CMD_SENSOR_STATE_WARNING] = "Warning",
55 [MC_CMD_SENSOR_STATE_FATAL] = "Fatal",
56 [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure",
57};
58
59void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev)
60{
61 unsigned int type, state, value;
62 const char *name = NULL, *state_txt;
63
64 type = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR);
65 state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE);
66 value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE);
67
68 /* Deal gracefully with the board having more drivers than we
69 * know about, but do not expect new sensor states. */
70 if (type < ARRAY_SIZE(efx_mcdi_sensor_type))
71 name = efx_mcdi_sensor_type[type].label;
72 if (!name)
73 name = "No sensor name available";
74 EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names));
75 state_txt = sensor_status_names[state];
76
77 netif_err(efx, hw, efx->net_dev,
78 "Sensor %d (%s) reports condition '%s' for raw value %d\n",
79 type, name, state_txt, value);
80}
81
82#ifdef CONFIG_SFC_MCDI_MON
83
84struct efx_mcdi_mon_attribute {
85 struct device_attribute dev_attr;
86 unsigned int index;
87 unsigned int type;
88 unsigned int limit_value;
89 char name[12];
90};
91
92static int efx_mcdi_mon_update(struct efx_nic *efx)
93{
94 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
95 u8 inbuf[MC_CMD_READ_SENSORS_IN_LEN];
96 int rc;
97
98 MCDI_SET_DWORD(inbuf, READ_SENSORS_IN_DMA_ADDR_LO,
99 hwmon->dma_buf.dma_addr & 0xffffffff);
100 MCDI_SET_DWORD(inbuf, READ_SENSORS_IN_DMA_ADDR_HI,
101 (u64)hwmon->dma_buf.dma_addr >> 32);
102
103 rc = efx_mcdi_rpc(efx, MC_CMD_READ_SENSORS,
104 inbuf, sizeof(inbuf), NULL, 0, NULL);
105 if (rc == 0)
106 hwmon->last_update = jiffies;
107 return rc;
108}
109
110static ssize_t efx_mcdi_mon_show_name(struct device *dev,
111 struct device_attribute *attr,
112 char *buf)
113{
114 return sprintf(buf, "%s\n", KBUILD_MODNAME);
115}
116
117static int efx_mcdi_mon_get_entry(struct device *dev, unsigned int index,
118 efx_dword_t *entry)
119{
120 struct efx_nic *efx = dev_get_drvdata(dev);
121 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
122 int rc;
123
124 BUILD_BUG_ON(MC_CMD_READ_SENSORS_OUT_LEN != 0);
125
126 mutex_lock(&hwmon->update_lock);
127
128 /* Use cached value if last update was < 1 s ago */
129 if (time_before(jiffies, hwmon->last_update + HZ))
130 rc = 0;
131 else
132 rc = efx_mcdi_mon_update(efx);
133
134 /* Copy out the requested entry */
135 *entry = ((efx_dword_t *)hwmon->dma_buf.addr)[index];
136
137 mutex_unlock(&hwmon->update_lock);
138
139 return rc;
140}
141
142static ssize_t efx_mcdi_mon_show_value(struct device *dev,
143 struct device_attribute *attr,
144 char *buf)
145{
146 struct efx_mcdi_mon_attribute *mon_attr =
147 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
148 efx_dword_t entry;
149 unsigned int value;
150 int rc;
151
152 rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
153 if (rc)
154 return rc;
155
156 value = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_VALUE);
157
158 /* Convert temperature from degrees to milli-degrees Celsius */
159 if (efx_mcdi_sensor_type[mon_attr->type].hwmon_type == EFX_HWMON_TEMP)
160 value *= 1000;
161
162 return sprintf(buf, "%u\n", value);
163}
164
165static ssize_t efx_mcdi_mon_show_limit(struct device *dev,
166 struct device_attribute *attr,
167 char *buf)
168{
169 struct efx_mcdi_mon_attribute *mon_attr =
170 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
171 unsigned int value;
172
173 value = mon_attr->limit_value;
174
175 /* Convert temperature from degrees to milli-degrees Celsius */
176 if (efx_mcdi_sensor_type[mon_attr->type].hwmon_type == EFX_HWMON_TEMP)
177 value *= 1000;
178
179 return sprintf(buf, "%u\n", value);
180}
181
182static ssize_t efx_mcdi_mon_show_alarm(struct device *dev,
183 struct device_attribute *attr,
184 char *buf)
185{
186 struct efx_mcdi_mon_attribute *mon_attr =
187 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
188 efx_dword_t entry;
189 int state;
190 int rc;
191
192 rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
193 if (rc)
194 return rc;
195
196 state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE);
197 return sprintf(buf, "%d\n", state != MC_CMD_SENSOR_STATE_OK);
198}
199
200static ssize_t efx_mcdi_mon_show_label(struct device *dev,
201 struct device_attribute *attr,
202 char *buf)
203{
204 struct efx_mcdi_mon_attribute *mon_attr =
205 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
206 return sprintf(buf, "%s\n",
207 efx_mcdi_sensor_type[mon_attr->type].label);
208}
209
210static int
211efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name,
212 ssize_t (*reader)(struct device *,
213 struct device_attribute *, char *),
214 unsigned int index, unsigned int type,
215 unsigned int limit_value)
216{
217 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
218 struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs];
219 int rc;
220
221 strlcpy(attr->name, name, sizeof(attr->name));
222 attr->index = index;
223 attr->type = type;
224 attr->limit_value = limit_value;
225 attr->dev_attr.attr.name = attr->name;
226 attr->dev_attr.attr.mode = S_IRUGO;
227 attr->dev_attr.show = reader;
228 rc = device_create_file(&efx->pci_dev->dev, &attr->dev_attr);
229 if (rc == 0)
230 ++hwmon->n_attrs;
231 return rc;
232}
233
234int efx_mcdi_mon_probe(struct efx_nic *efx)
235{
236 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
237 unsigned int n_attrs, n_temp = 0, n_cool = 0, n_in = 0;
238 u8 outbuf[MC_CMD_SENSOR_INFO_OUT_LENMAX];
239 size_t outlen;
240 char name[12];
241 u32 mask;
242 int rc, i, type;
243
244 BUILD_BUG_ON(MC_CMD_SENSOR_INFO_IN_LEN != 0);
245
246 rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO, NULL, 0,
247 outbuf, sizeof(outbuf), &outlen);
248 if (rc)
249 return rc;
250 if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN)
251 return -EIO;
252
253 /* Find out which sensors are present. Don't create a device
254 * if there are none.
255 */
256 mask = MCDI_DWORD(outbuf, SENSOR_INFO_OUT_MASK);
257 if (mask == 0)
258 return 0;
259
260 /* Check again for short response */
261 if (outlen < MC_CMD_SENSOR_INFO_OUT_LEN(hweight32(mask)))
262 return -EIO;
263
264 rc = efx_nic_alloc_buffer(efx, &hwmon->dma_buf,
265 4 * MC_CMD_SENSOR_ENTRY_MAXNUM);
266 if (rc)
267 return rc;
268
269 mutex_init(&hwmon->update_lock);
270 efx_mcdi_mon_update(efx);
271
272 /* Allocate space for the maximum possible number of
273 * attributes for this set of sensors: name of the driver plus
274 * value, min, max, crit, alarm and label for each sensor.
275 */
276 n_attrs = 1 + 6 * hweight32(mask);
277 hwmon->attrs = kcalloc(n_attrs, sizeof(*hwmon->attrs), GFP_KERNEL);
278 if (!hwmon->attrs) {
279 rc = -ENOMEM;
280 goto fail;
281 }
282
283 hwmon->device = hwmon_device_register(&efx->pci_dev->dev);
284 if (IS_ERR(hwmon->device)) {
285 rc = PTR_ERR(hwmon->device);
286 goto fail;
287 }
288
289 rc = efx_mcdi_mon_add_attr(efx, "name", efx_mcdi_mon_show_name, 0, 0, 0);
290 if (rc)
291 goto fail;
292
293 for (i = 0, type = -1; ; i++) {
294 const char *hwmon_prefix;
295 unsigned hwmon_index;
296 u16 min1, max1, min2, max2;
297
298 /* Find next sensor type or exit if there is none */
299 type++;
300 while (!(mask & (1 << type))) {
301 type++;
302 if (type == 32)
303 return 0;
304 }
305
306 /* Skip sensors specific to a different port */
307 if (efx_mcdi_sensor_type[type].hwmon_type != EFX_HWMON_UNKNOWN &&
308 efx_mcdi_sensor_type[type].port >= 0 &&
309 efx_mcdi_sensor_type[type].port != efx_port_num(efx))
310 continue;
311
312 switch (efx_mcdi_sensor_type[type].hwmon_type) {
313 case EFX_HWMON_TEMP:
314 hwmon_prefix = "temp";
315 hwmon_index = ++n_temp; /* 1-based */
316 break;
317 case EFX_HWMON_COOL:
318 /* This is likely to be a heatsink, but there
319 * is no convention for representing cooling
320 * devices other than fans.
321 */
322 hwmon_prefix = "fan";
323 hwmon_index = ++n_cool; /* 1-based */
324 break;
325 default:
326 hwmon_prefix = "in";
327 hwmon_index = n_in++; /* 0-based */
328 break;
329 }
330
331 min1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
332 SENSOR_INFO_ENTRY, i, MIN1);
333 max1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
334 SENSOR_INFO_ENTRY, i, MAX1);
335 min2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
336 SENSOR_INFO_ENTRY, i, MIN2);
337 max2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
338 SENSOR_INFO_ENTRY, i, MAX2);
339
340 if (min1 != max1) {
341 snprintf(name, sizeof(name), "%s%u_input",
342 hwmon_prefix, hwmon_index);
343 rc = efx_mcdi_mon_add_attr(
344 efx, name, efx_mcdi_mon_show_value, i, type, 0);
345 if (rc)
346 goto fail;
347
348 snprintf(name, sizeof(name), "%s%u_min",
349 hwmon_prefix, hwmon_index);
350 rc = efx_mcdi_mon_add_attr(
351 efx, name, efx_mcdi_mon_show_limit,
352 i, type, min1);
353 if (rc)
354 goto fail;
355
356 snprintf(name, sizeof(name), "%s%u_max",
357 hwmon_prefix, hwmon_index);
358 rc = efx_mcdi_mon_add_attr(
359 efx, name, efx_mcdi_mon_show_limit,
360 i, type, max1);
361 if (rc)
362 goto fail;
363
364 if (min2 != max2) {
365 /* Assume max2 is critical value.
366 * But we have no good way to expose min2.
367 */
368 snprintf(name, sizeof(name), "%s%u_crit",
369 hwmon_prefix, hwmon_index);
370 rc = efx_mcdi_mon_add_attr(
371 efx, name, efx_mcdi_mon_show_limit,
372 i, type, max2);
373 if (rc)
374 goto fail;
375 }
376 }
377
378 snprintf(name, sizeof(name), "%s%u_alarm",
379 hwmon_prefix, hwmon_index);
380 rc = efx_mcdi_mon_add_attr(
381 efx, name, efx_mcdi_mon_show_alarm, i, type, 0);
382 if (rc)
383 goto fail;
384
385 if (efx_mcdi_sensor_type[type].label) {
386 snprintf(name, sizeof(name), "%s%u_label",
387 hwmon_prefix, hwmon_index);
388 rc = efx_mcdi_mon_add_attr(
389 efx, name, efx_mcdi_mon_show_label, i, type, 0);
390 if (rc)
391 goto fail;
392 }
393 }
394
395fail:
396 efx_mcdi_mon_remove(efx);
397 return rc;
398}
399
400void efx_mcdi_mon_remove(struct efx_nic *efx)
401{
402 struct siena_nic_data *nic_data = efx->nic_data;
403 struct efx_mcdi_mon *hwmon = &nic_data->hwmon;
404 unsigned int i;
405
406 for (i = 0; i < hwmon->n_attrs; i++)
407 device_remove_file(&efx->pci_dev->dev,
408 &hwmon->attrs[i].dev_attr);
409 kfree(hwmon->attrs);
410 if (hwmon->device)
411 hwmon_device_unregister(hwmon->device);
412 efx_nic_free_buffer(efx, &hwmon->dma_buf);
413}
414
415#endif /* CONFIG_SFC_MCDI_MON */