diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/sfc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi.c | 43 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi.h | 28 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi_mon.c | 415 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/nic.h | 14 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/siena.c | 6 |
7 files changed, 471 insertions, 44 deletions
diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig index 5d18841f0f3d..ae40b666739b 100644 --- a/drivers/net/ethernet/sfc/Kconfig +++ b/drivers/net/ethernet/sfc/Kconfig | |||
@@ -19,3 +19,10 @@ config SFC_MTD | |||
19 | This exposes the on-board flash memory as MTD devices (e.g. | 19 | This exposes the on-board flash memory as MTD devices (e.g. |
20 | /dev/mtd1). This makes it possible to upload new firmware | 20 | /dev/mtd1). This makes it possible to upload new firmware |
21 | to the NIC. | 21 | to the NIC. |
22 | config SFC_MCDI_MON | ||
23 | bool "Solarflare SFC9000-family hwmon support" | ||
24 | depends on SFC && HWMON && !(SFC=y && HWMON=m) | ||
25 | default y | ||
26 | ----help--- | ||
27 | This exposes the on-board firmware-managed sensors as a | ||
28 | hardware monitor device. | ||
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index ab31c7124db1..3fa2e25ccc45 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile | |||
@@ -2,7 +2,7 @@ sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o filter.o \ | |||
2 | falcon_xmac.o mcdi_mac.o \ | 2 | falcon_xmac.o mcdi_mac.o \ |
3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ | 3 | selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ |
4 | tenxpress.o txc43128_phy.o falcon_boards.o \ | 4 | tenxpress.o txc43128_phy.o falcon_boards.o \ |
5 | mcdi.o mcdi_phy.o | 5 | mcdi.o mcdi_phy.o mcdi_mon.o |
6 | sfc-$(CONFIG_SFC_MTD) += mtd.o | 6 | sfc-$(CONFIG_SFC_MTD) += mtd.o |
7 | 7 | ||
8 | obj-$(CONFIG_SFC) += sfc.o | 8 | obj-$(CONFIG_SFC) += sfc.o |
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index f1cad22b30fa..619f63a66ce7 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c | |||
@@ -517,49 +517,6 @@ static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) | |||
517 | efx_link_status_changed(efx); | 517 | efx_link_status_changed(efx); |
518 | } | 518 | } |
519 | 519 | ||
520 | static const char *const sensor_names[] = { | ||
521 | [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor", | ||
522 | [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor", | ||
523 | [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling", | ||
524 | [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor", | ||
525 | [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling", | ||
526 | [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor", | ||
527 | [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling", | ||
528 | [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor", | ||
529 | [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor", | ||
530 | [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor", | ||
531 | [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor", | ||
532 | [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor", | ||
533 | [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor" | ||
534 | }; | ||
535 | |||
536 | static const char *const sensor_status_names[] = { | ||
537 | [MC_CMD_SENSOR_STATE_OK] = "OK", | ||
538 | [MC_CMD_SENSOR_STATE_WARNING] = "Warning", | ||
539 | [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", | ||
540 | [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", | ||
541 | }; | ||
542 | |||
543 | static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) | ||
544 | { | ||
545 | unsigned int monitor, state, value; | ||
546 | const char *name, *state_txt; | ||
547 | monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); | ||
548 | state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); | ||
549 | value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); | ||
550 | /* Deal gracefully with the board having more drivers than we | ||
551 | * know about, but do not expect new sensor states. */ | ||
552 | name = (monitor >= ARRAY_SIZE(sensor_names)) | ||
553 | ? "No sensor name available" : | ||
554 | sensor_names[monitor]; | ||
555 | EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); | ||
556 | state_txt = sensor_status_names[state]; | ||
557 | |||
558 | netif_err(efx, hw, efx->net_dev, | ||
559 | "Sensor %d (%s) reports condition '%s' for raw value %d\n", | ||
560 | monitor, name, state_txt, value); | ||
561 | } | ||
562 | |||
563 | /* Called from falcon_process_eventq for MCDI events */ | 520 | /* Called from falcon_process_eventq for MCDI events */ |
564 | void efx_mcdi_process_event(struct efx_channel *channel, | 521 | void efx_mcdi_process_event(struct efx_channel *channel, |
565 | efx_qword_t *event) | 522 | efx_qword_t *event) |
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 4dd39fcca678..fbaa6efcd744 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h | |||
@@ -56,6 +56,15 @@ struct efx_mcdi_iface { | |||
56 | size_t resplen; | 56 | size_t resplen; |
57 | }; | 57 | }; |
58 | 58 | ||
59 | struct efx_mcdi_mon { | ||
60 | struct efx_buffer dma_buf; | ||
61 | struct mutex update_lock; | ||
62 | unsigned long last_update; | ||
63 | struct device *device; | ||
64 | struct efx_mcdi_mon_attribute *attrs; | ||
65 | unsigned int n_attrs; | ||
66 | }; | ||
67 | |||
59 | extern void efx_mcdi_init(struct efx_nic *efx); | 68 | extern void efx_mcdi_init(struct efx_nic *efx); |
60 | 69 | ||
61 | extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, | 70 | extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, |
@@ -68,6 +77,7 @@ extern void efx_mcdi_mode_event(struct efx_nic *efx); | |||
68 | 77 | ||
69 | extern void efx_mcdi_process_event(struct efx_channel *channel, | 78 | extern void efx_mcdi_process_event(struct efx_channel *channel, |
70 | efx_qword_t *event); | 79 | efx_qword_t *event); |
80 | extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); | ||
71 | 81 | ||
72 | #define MCDI_PTR2(_buf, _ofst) \ | 82 | #define MCDI_PTR2(_buf, _ofst) \ |
73 | (((u8 *)_buf) + _ofst) | 83 | (((u8 *)_buf) + _ofst) |
@@ -83,6 +93,10 @@ extern void efx_mcdi_process_event(struct efx_channel *channel, | |||
83 | 93 | ||
84 | #define MCDI_PTR(_buf, _ofst) \ | 94 | #define MCDI_PTR(_buf, _ofst) \ |
85 | MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) | 95 | MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) |
96 | #define MCDI_ARRAY_PTR(_buf, _field, _type, _index) \ | ||
97 | MCDI_PTR2(_buf, \ | ||
98 | MC_CMD_ ## _field ## _OFST + \ | ||
99 | (_index) * MC_CMD_ ## _type ## _TYPEDEF_LEN) | ||
86 | #define MCDI_SET_DWORD(_buf, _ofst, _value) \ | 100 | #define MCDI_SET_DWORD(_buf, _ofst, _value) \ |
87 | MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) | 101 | MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) |
88 | #define MCDI_DWORD(_buf, _ofst) \ | 102 | #define MCDI_DWORD(_buf, _ofst) \ |
@@ -92,6 +106,12 @@ extern void efx_mcdi_process_event(struct efx_channel *channel, | |||
92 | 106 | ||
93 | #define MCDI_EVENT_FIELD(_ev, _field) \ | 107 | #define MCDI_EVENT_FIELD(_ev, _field) \ |
94 | EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) | 108 | EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) |
109 | #define MCDI_ARRAY_FIELD(_buf, _field1, _type, _index, _field2) \ | ||
110 | EFX_DWORD_FIELD( \ | ||
111 | *((efx_dword_t *) \ | ||
112 | (MCDI_ARRAY_PTR(_buf, _field1, _type, _index) + \ | ||
113 | (MC_CMD_ ## _type ## _TYPEDEF_ ## _field2 ## _OFST & ~3))), \ | ||
114 | MC_CMD_ ## _type ## _TYPEDEF_ ## _field2) | ||
95 | 115 | ||
96 | extern void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len); | 116 | extern void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len); |
97 | extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, | 117 | extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, |
@@ -131,4 +151,12 @@ extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, | |||
131 | extern int efx_mcdi_mac_reconfigure(struct efx_nic *efx); | 151 | extern int efx_mcdi_mac_reconfigure(struct efx_nic *efx); |
132 | extern bool efx_mcdi_mac_check_fault(struct efx_nic *efx); | 152 | extern bool efx_mcdi_mac_check_fault(struct efx_nic *efx); |
133 | 153 | ||
154 | #ifdef CONFIG_SFC_MCDI_MON | ||
155 | extern int efx_mcdi_mon_probe(struct efx_nic *efx); | ||
156 | extern void efx_mcdi_mon_remove(struct efx_nic *efx); | ||
157 | #else | ||
158 | static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; } | ||
159 | static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {} | ||
160 | #endif | ||
161 | |||
134 | #endif /* EFX_MCDI_H */ | 162 | #endif /* EFX_MCDI_H */ |
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 | |||
20 | enum 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 | |||
27 | static 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 | |||
52 | static 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 | |||
59 | void 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 | |||
84 | struct 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 | |||
92 | static 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 | |||
110 | static 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 | |||
117 | static 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 | |||
142 | static 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 | |||
165 | static 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 | |||
182 | static 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 | |||
200 | static 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 | |||
210 | static int | ||
211 | efx_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 | |||
234 | int 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 | |||
395 | fail: | ||
396 | efx_mcdi_mon_remove(efx); | ||
397 | return rc; | ||
398 | } | ||
399 | |||
400 | void 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 */ | ||
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index a3ccd0b9d78d..905a1877d603 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h | |||
@@ -144,12 +144,26 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx) | |||
144 | * struct siena_nic_data - Siena NIC state | 144 | * struct siena_nic_data - Siena NIC state |
145 | * @mcdi: Management-Controller-to-Driver Interface | 145 | * @mcdi: Management-Controller-to-Driver Interface |
146 | * @wol_filter_id: Wake-on-LAN packet filter id | 146 | * @wol_filter_id: Wake-on-LAN packet filter id |
147 | * @hwmon: Hardware monitor state | ||
147 | */ | 148 | */ |
148 | struct siena_nic_data { | 149 | struct siena_nic_data { |
149 | struct efx_mcdi_iface mcdi; | 150 | struct efx_mcdi_iface mcdi; |
150 | int wol_filter_id; | 151 | int wol_filter_id; |
152 | #ifdef CONFIG_SFC_MCDI_MON | ||
153 | struct efx_mcdi_mon hwmon; | ||
154 | #endif | ||
151 | }; | 155 | }; |
152 | 156 | ||
157 | #ifdef CONFIG_SFC_MCDI_MON | ||
158 | static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx) | ||
159 | { | ||
160 | struct siena_nic_data *nic_data; | ||
161 | EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0); | ||
162 | nic_data = efx->nic_data; | ||
163 | return &nic_data->hwmon; | ||
164 | } | ||
165 | #endif | ||
166 | |||
153 | extern const struct efx_nic_type falcon_a1_nic_type; | 167 | extern const struct efx_nic_type falcon_a1_nic_type; |
154 | extern const struct efx_nic_type falcon_b0_nic_type; | 168 | extern const struct efx_nic_type falcon_b0_nic_type; |
155 | extern const struct efx_nic_type siena_a0_nic_type; | 169 | extern const struct efx_nic_type siena_a0_nic_type; |
diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index f05425842b31..d3c4169e2a0b 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c | |||
@@ -300,6 +300,10 @@ static int siena_probe_nic(struct efx_nic *efx) | |||
300 | goto fail5; | 300 | goto fail5; |
301 | } | 301 | } |
302 | 302 | ||
303 | rc = efx_mcdi_mon_probe(efx); | ||
304 | if (rc) | ||
305 | goto fail5; | ||
306 | |||
303 | return 0; | 307 | return 0; |
304 | 308 | ||
305 | fail5: | 309 | fail5: |
@@ -387,6 +391,8 @@ static int siena_init_nic(struct efx_nic *efx) | |||
387 | 391 | ||
388 | static void siena_remove_nic(struct efx_nic *efx) | 392 | static void siena_remove_nic(struct efx_nic *efx) |
389 | { | 393 | { |
394 | efx_mcdi_mon_remove(efx); | ||
395 | |||
390 | efx_nic_free_buffer(efx, &efx->irq_status); | 396 | efx_nic_free_buffer(efx, &efx->irq_status); |
391 | 397 | ||
392 | siena_reset_hw(efx, RESET_TYPE_ALL); | 398 | siena_reset_hw(efx, RESET_TYPE_ALL); |