blob: 3179b2ba316ef4fa06e0edda8759e004a6b2f411 [file] [log] [blame]
Ben Hutchings55c5e0f82012-01-06 20:25:39 +00001/****************************************************************************
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),
Ben Hutchings2d0cc562012-02-17 00:10:45 +000040 SENSOR(PHY1_COOLING, "PHY cooling", EFX_HWMON_COOL, 1),
Ben Hutchings55c5e0f82012-01-06 20:25:39 +000041 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);
Ben Hutchings59cfc472012-09-14 17:30:10 +010095 MCDI_DECLARE_BUF(inbuf, MC_CMD_READ_SENSORS_IN_LEN);
Ben Hutchings55c5e0f82012-01-06 20:25:39 +000096 int rc;
97
Ben Hutchings338f74d2012-10-10 23:20:17 +010098 MCDI_SET_QWORD(inbuf, READ_SENSORS_IN_DMA_ADDR,
99 hwmon->dma_buf.dma_addr);
Ben Hutchings55c5e0f82012-01-06 20:25:39 +0000100
101 rc = efx_mcdi_rpc(efx, MC_CMD_READ_SENSORS,
102 inbuf, sizeof(inbuf), NULL, 0, NULL);
103 if (rc == 0)
104 hwmon->last_update = jiffies;
105 return rc;
106}
107
108static ssize_t efx_mcdi_mon_show_name(struct device *dev,
109 struct device_attribute *attr,
110 char *buf)
111{
112 return sprintf(buf, "%s\n", KBUILD_MODNAME);
113}
114
115static int efx_mcdi_mon_get_entry(struct device *dev, unsigned int index,
116 efx_dword_t *entry)
117{
118 struct efx_nic *efx = dev_get_drvdata(dev);
119 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
120 int rc;
121
122 BUILD_BUG_ON(MC_CMD_READ_SENSORS_OUT_LEN != 0);
123
124 mutex_lock(&hwmon->update_lock);
125
126 /* Use cached value if last update was < 1 s ago */
127 if (time_before(jiffies, hwmon->last_update + HZ))
128 rc = 0;
129 else
130 rc = efx_mcdi_mon_update(efx);
131
132 /* Copy out the requested entry */
133 *entry = ((efx_dword_t *)hwmon->dma_buf.addr)[index];
134
135 mutex_unlock(&hwmon->update_lock);
136
137 return rc;
138}
139
140static ssize_t efx_mcdi_mon_show_value(struct device *dev,
141 struct device_attribute *attr,
142 char *buf)
143{
144 struct efx_mcdi_mon_attribute *mon_attr =
145 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
146 efx_dword_t entry;
147 unsigned int value;
148 int rc;
149
150 rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
151 if (rc)
152 return rc;
153
154 value = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_VALUE);
155
156 /* Convert temperature from degrees to milli-degrees Celsius */
157 if (efx_mcdi_sensor_type[mon_attr->type].hwmon_type == EFX_HWMON_TEMP)
158 value *= 1000;
159
160 return sprintf(buf, "%u\n", value);
161}
162
163static ssize_t efx_mcdi_mon_show_limit(struct device *dev,
164 struct device_attribute *attr,
165 char *buf)
166{
167 struct efx_mcdi_mon_attribute *mon_attr =
168 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
169 unsigned int value;
170
171 value = mon_attr->limit_value;
172
173 /* Convert temperature from degrees to milli-degrees Celsius */
174 if (efx_mcdi_sensor_type[mon_attr->type].hwmon_type == EFX_HWMON_TEMP)
175 value *= 1000;
176
177 return sprintf(buf, "%u\n", value);
178}
179
180static ssize_t efx_mcdi_mon_show_alarm(struct device *dev,
181 struct device_attribute *attr,
182 char *buf)
183{
184 struct efx_mcdi_mon_attribute *mon_attr =
185 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
186 efx_dword_t entry;
187 int state;
188 int rc;
189
190 rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
191 if (rc)
192 return rc;
193
194 state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE);
195 return sprintf(buf, "%d\n", state != MC_CMD_SENSOR_STATE_OK);
196}
197
198static ssize_t efx_mcdi_mon_show_label(struct device *dev,
199 struct device_attribute *attr,
200 char *buf)
201{
202 struct efx_mcdi_mon_attribute *mon_attr =
203 container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
204 return sprintf(buf, "%s\n",
205 efx_mcdi_sensor_type[mon_attr->type].label);
206}
207
208static int
209efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name,
210 ssize_t (*reader)(struct device *,
211 struct device_attribute *, char *),
212 unsigned int index, unsigned int type,
213 unsigned int limit_value)
214{
215 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
216 struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs];
217 int rc;
218
219 strlcpy(attr->name, name, sizeof(attr->name));
220 attr->index = index;
221 attr->type = type;
222 attr->limit_value = limit_value;
Michal Schmidta9ec6bd2012-07-19 07:04:45 +0000223 sysfs_attr_init(&attr->dev_attr.attr);
Ben Hutchings55c5e0f82012-01-06 20:25:39 +0000224 attr->dev_attr.attr.name = attr->name;
225 attr->dev_attr.attr.mode = S_IRUGO;
226 attr->dev_attr.show = reader;
227 rc = device_create_file(&efx->pci_dev->dev, &attr->dev_attr);
228 if (rc == 0)
229 ++hwmon->n_attrs;
230 return rc;
231}
232
233int efx_mcdi_mon_probe(struct efx_nic *efx)
234{
235 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
236 unsigned int n_attrs, n_temp = 0, n_cool = 0, n_in = 0;
Ben Hutchings59cfc472012-09-14 17:30:10 +0100237 MCDI_DECLARE_BUF(outbuf, MC_CMD_SENSOR_INFO_OUT_LENMAX);
Ben Hutchings55c5e0f82012-01-06 20:25:39 +0000238 size_t outlen;
239 char name[12];
240 u32 mask;
241 int rc, i, type;
242
243 BUILD_BUG_ON(MC_CMD_SENSOR_INFO_IN_LEN != 0);
244
245 rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO, NULL, 0,
246 outbuf, sizeof(outbuf), &outlen);
247 if (rc)
248 return rc;
249 if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN)
250 return -EIO;
251
252 /* Find out which sensors are present. Don't create a device
253 * if there are none.
254 */
255 mask = MCDI_DWORD(outbuf, SENSOR_INFO_OUT_MASK);
256 if (mask == 0)
257 return 0;
258
259 /* Check again for short response */
260 if (outlen < MC_CMD_SENSOR_INFO_OUT_LEN(hweight32(mask)))
261 return -EIO;
262
263 rc = efx_nic_alloc_buffer(efx, &hwmon->dma_buf,
264 4 * MC_CMD_SENSOR_ENTRY_MAXNUM);
265 if (rc)
266 return rc;
267
268 mutex_init(&hwmon->update_lock);
269 efx_mcdi_mon_update(efx);
270
271 /* Allocate space for the maximum possible number of
272 * attributes for this set of sensors: name of the driver plus
273 * value, min, max, crit, alarm and label for each sensor.
274 */
275 n_attrs = 1 + 6 * hweight32(mask);
276 hwmon->attrs = kcalloc(n_attrs, sizeof(*hwmon->attrs), GFP_KERNEL);
277 if (!hwmon->attrs) {
278 rc = -ENOMEM;
279 goto fail;
280 }
281
282 hwmon->device = hwmon_device_register(&efx->pci_dev->dev);
283 if (IS_ERR(hwmon->device)) {
284 rc = PTR_ERR(hwmon->device);
285 goto fail;
286 }
287
288 rc = efx_mcdi_mon_add_attr(efx, "name", efx_mcdi_mon_show_name, 0, 0, 0);
289 if (rc)
290 goto fail;
291
292 for (i = 0, type = -1; ; i++) {
293 const char *hwmon_prefix;
294 unsigned hwmon_index;
295 u16 min1, max1, min2, max2;
296
297 /* Find next sensor type or exit if there is none */
298 type++;
299 while (!(mask & (1 << type))) {
300 type++;
301 if (type == 32)
302 return 0;
303 }
304
305 /* Skip sensors specific to a different port */
306 if (efx_mcdi_sensor_type[type].hwmon_type != EFX_HWMON_UNKNOWN &&
307 efx_mcdi_sensor_type[type].port >= 0 &&
308 efx_mcdi_sensor_type[type].port != efx_port_num(efx))
309 continue;
310
311 switch (efx_mcdi_sensor_type[type].hwmon_type) {
312 case EFX_HWMON_TEMP:
313 hwmon_prefix = "temp";
314 hwmon_index = ++n_temp; /* 1-based */
315 break;
316 case EFX_HWMON_COOL:
317 /* This is likely to be a heatsink, but there
318 * is no convention for representing cooling
319 * devices other than fans.
320 */
321 hwmon_prefix = "fan";
322 hwmon_index = ++n_cool; /* 1-based */
323 break;
324 default:
325 hwmon_prefix = "in";
326 hwmon_index = n_in++; /* 0-based */
327 break;
328 }
329
330 min1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
331 SENSOR_INFO_ENTRY, i, MIN1);
332 max1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
333 SENSOR_INFO_ENTRY, i, MAX1);
334 min2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
335 SENSOR_INFO_ENTRY, i, MIN2);
336 max2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
337 SENSOR_INFO_ENTRY, i, MAX2);
338
339 if (min1 != max1) {
340 snprintf(name, sizeof(name), "%s%u_input",
341 hwmon_prefix, hwmon_index);
342 rc = efx_mcdi_mon_add_attr(
343 efx, name, efx_mcdi_mon_show_value, i, type, 0);
344 if (rc)
345 goto fail;
346
347 snprintf(name, sizeof(name), "%s%u_min",
348 hwmon_prefix, hwmon_index);
349 rc = efx_mcdi_mon_add_attr(
350 efx, name, efx_mcdi_mon_show_limit,
351 i, type, min1);
352 if (rc)
353 goto fail;
354
355 snprintf(name, sizeof(name), "%s%u_max",
356 hwmon_prefix, hwmon_index);
357 rc = efx_mcdi_mon_add_attr(
358 efx, name, efx_mcdi_mon_show_limit,
359 i, type, max1);
360 if (rc)
361 goto fail;
362
363 if (min2 != max2) {
364 /* Assume max2 is critical value.
365 * But we have no good way to expose min2.
366 */
367 snprintf(name, sizeof(name), "%s%u_crit",
368 hwmon_prefix, hwmon_index);
369 rc = efx_mcdi_mon_add_attr(
370 efx, name, efx_mcdi_mon_show_limit,
371 i, type, max2);
372 if (rc)
373 goto fail;
374 }
375 }
376
377 snprintf(name, sizeof(name), "%s%u_alarm",
378 hwmon_prefix, hwmon_index);
379 rc = efx_mcdi_mon_add_attr(
380 efx, name, efx_mcdi_mon_show_alarm, i, type, 0);
381 if (rc)
382 goto fail;
383
384 if (efx_mcdi_sensor_type[type].label) {
385 snprintf(name, sizeof(name), "%s%u_label",
386 hwmon_prefix, hwmon_index);
387 rc = efx_mcdi_mon_add_attr(
388 efx, name, efx_mcdi_mon_show_label, i, type, 0);
389 if (rc)
390 goto fail;
391 }
392 }
393
394fail:
395 efx_mcdi_mon_remove(efx);
396 return rc;
397}
398
399void efx_mcdi_mon_remove(struct efx_nic *efx)
400{
Ben Hutchingse847b532012-09-13 01:11:24 +0100401 struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
Ben Hutchings55c5e0f82012-01-06 20:25:39 +0000402 unsigned int i;
403
404 for (i = 0; i < hwmon->n_attrs; i++)
405 device_remove_file(&efx->pci_dev->dev,
406 &hwmon->attrs[i].dev_attr);
407 kfree(hwmon->attrs);
408 if (hwmon->device)
409 hwmon_device_unregister(hwmon->device);
410 efx_nic_free_buffer(efx, &hwmon->dma_buf);
411}
412
413#endif /* CONFIG_SFC_MCDI_MON */