Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /****************************************************************************
0003  * Driver for Solarflare network controllers and boards
0004  * Copyright 2011-2013 Solarflare Communications Inc.
0005  */
0006 
0007 #include <linux/bitops.h>
0008 #include <linux/slab.h>
0009 #include <linux/hwmon.h>
0010 #include <linux/stat.h>
0011 
0012 #include "net_driver.h"
0013 #include "mcdi.h"
0014 #include "mcdi_pcol.h"
0015 #include "nic.h"
0016 
0017 enum efx_hwmon_type {
0018     EFX_HWMON_UNKNOWN,
0019     EFX_HWMON_TEMP,         /* temperature */
0020     EFX_HWMON_COOL,         /* cooling device, probably a heatsink */
0021     EFX_HWMON_IN,       /* voltage */
0022     EFX_HWMON_CURR,     /* current */
0023     EFX_HWMON_POWER,    /* power */
0024     EFX_HWMON_TYPES_COUNT
0025 };
0026 
0027 static const char *const efx_hwmon_unit[EFX_HWMON_TYPES_COUNT] = {
0028     [EFX_HWMON_TEMP]  = " degC",
0029     [EFX_HWMON_COOL]  = " rpm", /* though nonsense for a heatsink */
0030     [EFX_HWMON_IN]    = " mV",
0031     [EFX_HWMON_CURR]  = " mA",
0032     [EFX_HWMON_POWER] = " W",
0033 };
0034 
0035 static const struct {
0036     const char *label;
0037     enum efx_hwmon_type hwmon_type;
0038     int port;
0039 } efx_mcdi_sensor_type[] = {
0040 #define SENSOR(name, label, hwmon_type, port)               \
0041     [MC_CMD_SENSOR_##name] = { label, EFX_HWMON_ ## hwmon_type, port }
0042     SENSOR(CONTROLLER_TEMP,     "Controller board temp.",   TEMP,  -1),
0043     SENSOR(PHY_COMMON_TEMP,     "PHY temp.",            TEMP,  -1),
0044     SENSOR(CONTROLLER_COOLING,  "Controller heat sink",     COOL,  -1),
0045     SENSOR(PHY0_TEMP,       "PHY temp.",            TEMP,  0),
0046     SENSOR(PHY0_COOLING,        "PHY heat sink",        COOL,  0),
0047     SENSOR(PHY1_TEMP,       "PHY temp.",            TEMP,  1),
0048     SENSOR(PHY1_COOLING,        "PHY heat sink",        COOL,  1),
0049     SENSOR(IN_1V0,          "1.0V supply",          IN,    -1),
0050     SENSOR(IN_1V2,          "1.2V supply",          IN,    -1),
0051     SENSOR(IN_1V8,          "1.8V supply",          IN,    -1),
0052     SENSOR(IN_2V5,          "2.5V supply",          IN,    -1),
0053     SENSOR(IN_3V3,          "3.3V supply",          IN,    -1),
0054     SENSOR(IN_12V0,         "12.0V supply",         IN,    -1),
0055     SENSOR(IN_1V2A,         "1.2V analogue supply",     IN,    -1),
0056     SENSOR(IN_VREF,         "Ref. voltage",         IN,    -1),
0057     SENSOR(OUT_VAOE,        "AOE FPGA supply",      IN,    -1),
0058     SENSOR(AOE_TEMP,        "AOE FPGA temp.",       TEMP,  -1),
0059     SENSOR(PSU_AOE_TEMP,        "AOE regulator temp.",      TEMP,  -1),
0060     SENSOR(PSU_TEMP,        "Controller regulator temp.",
0061                                     TEMP,  -1),
0062     SENSOR(FAN_0,           "Fan 0",            COOL,  -1),
0063     SENSOR(FAN_1,           "Fan 1",            COOL,  -1),
0064     SENSOR(FAN_2,           "Fan 2",            COOL,  -1),
0065     SENSOR(FAN_3,           "Fan 3",            COOL,  -1),
0066     SENSOR(FAN_4,           "Fan 4",            COOL,  -1),
0067     SENSOR(IN_VAOE,         "AOE input supply",     IN,    -1),
0068     SENSOR(OUT_IAOE,        "AOE output current",       CURR,  -1),
0069     SENSOR(IN_IAOE,         "AOE input current",        CURR,  -1),
0070     SENSOR(NIC_POWER,       "Board power use",      POWER, -1),
0071     SENSOR(IN_0V9,          "0.9V supply",          IN,    -1),
0072     SENSOR(IN_I0V9,         "0.9V supply current",      CURR,  -1),
0073     SENSOR(IN_I1V2,         "1.2V supply current",      CURR,  -1),
0074     SENSOR(IN_0V9_ADC,      "0.9V supply (ext. ADC)",   IN,    -1),
0075     SENSOR(CONTROLLER_2_TEMP,   "Controller board temp. 2", TEMP,  -1),
0076     SENSOR(VREG_INTERNAL_TEMP,  "Regulator die temp.",      TEMP,  -1),
0077     SENSOR(VREG_0V9_TEMP,       "0.9V regulator temp.",     TEMP,  -1),
0078     SENSOR(VREG_1V2_TEMP,       "1.2V regulator temp.",     TEMP,  -1),
0079     SENSOR(CONTROLLER_VPTAT,
0080                   "Controller PTAT voltage (int. ADC)", IN,    -1),
0081     SENSOR(CONTROLLER_INTERNAL_TEMP,
0082                  "Controller die temp. (int. ADC)", TEMP,  -1),
0083     SENSOR(CONTROLLER_VPTAT_EXTADC,
0084                   "Controller PTAT voltage (ext. ADC)", IN,    -1),
0085     SENSOR(CONTROLLER_INTERNAL_TEMP_EXTADC,
0086                  "Controller die temp. (ext. ADC)", TEMP,  -1),
0087     SENSOR(AMBIENT_TEMP,        "Ambient temp.",        TEMP,  -1),
0088     SENSOR(AIRFLOW,         "Air flow raw",         IN,    -1),
0089     SENSOR(VDD08D_VSS08D_CSR,   "0.9V die (int. ADC)",      IN,    -1),
0090     SENSOR(VDD08D_VSS08D_CSR_EXTADC, "0.9V die (ext. ADC)",     IN,    -1),
0091     SENSOR(HOTPOINT_TEMP,  "Controller board temp. (hotpoint)", TEMP,  -1),
0092 #undef SENSOR
0093 };
0094 
0095 static const char *const sensor_status_names[] = {
0096     [MC_CMD_SENSOR_STATE_OK] = "OK",
0097     [MC_CMD_SENSOR_STATE_WARNING] = "Warning",
0098     [MC_CMD_SENSOR_STATE_FATAL] = "Fatal",
0099     [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure",
0100     [MC_CMD_SENSOR_STATE_NO_READING] = "No reading",
0101 };
0102 
0103 void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev)
0104 {
0105     unsigned int type, state, value;
0106     enum efx_hwmon_type hwmon_type = EFX_HWMON_UNKNOWN;
0107     const char *name = NULL, *state_txt, *unit;
0108 
0109     type = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR);
0110     state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE);
0111     value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE);
0112 
0113     /* Deal gracefully with the board having more drivers than we
0114      * know about, but do not expect new sensor states. */
0115     if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) {
0116         name = efx_mcdi_sensor_type[type].label;
0117         hwmon_type = efx_mcdi_sensor_type[type].hwmon_type;
0118     }
0119     if (!name)
0120         name = "No sensor name available";
0121     EFX_WARN_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names));
0122     state_txt = sensor_status_names[state];
0123     EFX_WARN_ON_PARANOID(hwmon_type >= EFX_HWMON_TYPES_COUNT);
0124     unit = efx_hwmon_unit[hwmon_type];
0125     if (!unit)
0126         unit = "";
0127 
0128     netif_err(efx, hw, efx->net_dev,
0129           "Sensor %d (%s) reports condition '%s' for value %d%s\n",
0130           type, name, state_txt, value, unit);
0131 }
0132 
0133 #ifdef CONFIG_SFC_MCDI_MON
0134 
0135 struct efx_mcdi_mon_attribute {
0136     struct device_attribute dev_attr;
0137     unsigned int index;
0138     unsigned int type;
0139     enum efx_hwmon_type hwmon_type;
0140     unsigned int limit_value;
0141     char name[12];
0142 };
0143 
0144 static int efx_mcdi_mon_update(struct efx_nic *efx)
0145 {
0146     struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
0147     MCDI_DECLARE_BUF(inbuf, MC_CMD_READ_SENSORS_EXT_IN_LEN);
0148     int rc;
0149 
0150     MCDI_SET_QWORD(inbuf, READ_SENSORS_EXT_IN_DMA_ADDR,
0151                hwmon->dma_buf.dma_addr);
0152     MCDI_SET_DWORD(inbuf, READ_SENSORS_EXT_IN_LENGTH, hwmon->dma_buf.len);
0153 
0154     rc = efx_mcdi_rpc(efx, MC_CMD_READ_SENSORS,
0155               inbuf, sizeof(inbuf), NULL, 0, NULL);
0156     if (rc == 0)
0157         hwmon->last_update = jiffies;
0158     return rc;
0159 }
0160 
0161 static int efx_mcdi_mon_get_entry(struct device *dev, unsigned int index,
0162                   efx_dword_t *entry)
0163 {
0164     struct efx_nic *efx = dev_get_drvdata(dev->parent);
0165     struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
0166     int rc;
0167 
0168     BUILD_BUG_ON(MC_CMD_READ_SENSORS_OUT_LEN != 0);
0169 
0170     mutex_lock(&hwmon->update_lock);
0171 
0172     /* Use cached value if last update was < 1 s ago */
0173     if (time_before(jiffies, hwmon->last_update + HZ))
0174         rc = 0;
0175     else
0176         rc = efx_mcdi_mon_update(efx);
0177 
0178     /* Copy out the requested entry */
0179     *entry = ((efx_dword_t *)hwmon->dma_buf.addr)[index];
0180 
0181     mutex_unlock(&hwmon->update_lock);
0182 
0183     return rc;
0184 }
0185 
0186 static ssize_t efx_mcdi_mon_show_value(struct device *dev,
0187                        struct device_attribute *attr,
0188                        char *buf)
0189 {
0190     struct efx_mcdi_mon_attribute *mon_attr =
0191         container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
0192     efx_dword_t entry;
0193     unsigned int value, state;
0194     int rc;
0195 
0196     rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
0197     if (rc)
0198         return rc;
0199 
0200     state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE);
0201     if (state == MC_CMD_SENSOR_STATE_NO_READING)
0202         return -EBUSY;
0203 
0204     value = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_VALUE);
0205 
0206     switch (mon_attr->hwmon_type) {
0207     case EFX_HWMON_TEMP:
0208         /* Convert temperature from degrees to milli-degrees Celsius */
0209         value *= 1000;
0210         break;
0211     case EFX_HWMON_POWER:
0212         /* Convert power from watts to microwatts */
0213         value *= 1000000;
0214         break;
0215     default:
0216         /* No conversion needed */
0217         break;
0218     }
0219 
0220     return sprintf(buf, "%u\n", value);
0221 }
0222 
0223 static ssize_t efx_mcdi_mon_show_limit(struct device *dev,
0224                        struct device_attribute *attr,
0225                        char *buf)
0226 {
0227     struct efx_mcdi_mon_attribute *mon_attr =
0228         container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
0229     unsigned int value;
0230 
0231     value = mon_attr->limit_value;
0232 
0233     switch (mon_attr->hwmon_type) {
0234     case EFX_HWMON_TEMP:
0235         /* Convert temperature from degrees to milli-degrees Celsius */
0236         value *= 1000;
0237         break;
0238     case EFX_HWMON_POWER:
0239         /* Convert power from watts to microwatts */
0240         value *= 1000000;
0241         break;
0242     default:
0243         /* No conversion needed */
0244         break;
0245     }
0246 
0247     return sprintf(buf, "%u\n", value);
0248 }
0249 
0250 static ssize_t efx_mcdi_mon_show_alarm(struct device *dev,
0251                        struct device_attribute *attr,
0252                        char *buf)
0253 {
0254     struct efx_mcdi_mon_attribute *mon_attr =
0255         container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
0256     efx_dword_t entry;
0257     int state;
0258     int rc;
0259 
0260     rc = efx_mcdi_mon_get_entry(dev, mon_attr->index, &entry);
0261     if (rc)
0262         return rc;
0263 
0264     state = EFX_DWORD_FIELD(entry, MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE);
0265     return sprintf(buf, "%d\n", state != MC_CMD_SENSOR_STATE_OK);
0266 }
0267 
0268 static ssize_t efx_mcdi_mon_show_label(struct device *dev,
0269                        struct device_attribute *attr,
0270                        char *buf)
0271 {
0272     struct efx_mcdi_mon_attribute *mon_attr =
0273         container_of(attr, struct efx_mcdi_mon_attribute, dev_attr);
0274     return sprintf(buf, "%s\n",
0275                efx_mcdi_sensor_type[mon_attr->type].label);
0276 }
0277 
0278 static void
0279 efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name,
0280               ssize_t (*reader)(struct device *,
0281                     struct device_attribute *, char *),
0282               unsigned int index, unsigned int type,
0283               unsigned int limit_value)
0284 {
0285     struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
0286     struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs];
0287 
0288     strlcpy(attr->name, name, sizeof(attr->name));
0289     attr->index = index;
0290     attr->type = type;
0291     if (type < ARRAY_SIZE(efx_mcdi_sensor_type))
0292         attr->hwmon_type = efx_mcdi_sensor_type[type].hwmon_type;
0293     else
0294         attr->hwmon_type = EFX_HWMON_UNKNOWN;
0295     attr->limit_value = limit_value;
0296     sysfs_attr_init(&attr->dev_attr.attr);
0297     attr->dev_attr.attr.name = attr->name;
0298     attr->dev_attr.attr.mode = 0444;
0299     attr->dev_attr.show = reader;
0300     hwmon->group.attrs[hwmon->n_attrs++] = &attr->dev_attr.attr;
0301 }
0302 
0303 int efx_mcdi_mon_probe(struct efx_nic *efx)
0304 {
0305     unsigned int n_temp = 0, n_cool = 0, n_in = 0, n_curr = 0, n_power = 0;
0306     struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
0307     MCDI_DECLARE_BUF(inbuf, MC_CMD_SENSOR_INFO_EXT_IN_LEN);
0308     MCDI_DECLARE_BUF(outbuf, MC_CMD_SENSOR_INFO_OUT_LENMAX);
0309     unsigned int n_pages, n_sensors, n_attrs, page;
0310     size_t outlen;
0311     char name[12];
0312     u32 mask;
0313     int rc, i, j, type;
0314 
0315     /* Find out how many sensors are present */
0316     n_sensors = 0;
0317     page = 0;
0318     do {
0319         MCDI_SET_DWORD(inbuf, SENSOR_INFO_EXT_IN_PAGE, page);
0320 
0321         rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO, inbuf, sizeof(inbuf),
0322                   outbuf, sizeof(outbuf), &outlen);
0323         if (rc)
0324             return rc;
0325         if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN)
0326             return -EIO;
0327 
0328         mask = MCDI_DWORD(outbuf, SENSOR_INFO_OUT_MASK);
0329         n_sensors += hweight32(mask & ~(1 << MC_CMD_SENSOR_PAGE0_NEXT));
0330         ++page;
0331     } while (mask & (1 << MC_CMD_SENSOR_PAGE0_NEXT));
0332     n_pages = page;
0333 
0334     /* Don't create a device if there are none */
0335     if (n_sensors == 0)
0336         return 0;
0337 
0338     rc = efx_nic_alloc_buffer(
0339         efx, &hwmon->dma_buf,
0340         n_sensors * MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_LEN,
0341         GFP_KERNEL);
0342     if (rc)
0343         return rc;
0344 
0345     mutex_init(&hwmon->update_lock);
0346     efx_mcdi_mon_update(efx);
0347 
0348     /* Allocate space for the maximum possible number of
0349      * attributes for this set of sensors:
0350      * value, min, max, crit, alarm and label for each sensor.
0351      */
0352     n_attrs = 6 * n_sensors;
0353     hwmon->attrs = kcalloc(n_attrs, sizeof(*hwmon->attrs), GFP_KERNEL);
0354     if (!hwmon->attrs) {
0355         rc = -ENOMEM;
0356         goto fail;
0357     }
0358     hwmon->group.attrs = kcalloc(n_attrs + 1, sizeof(struct attribute *),
0359                      GFP_KERNEL);
0360     if (!hwmon->group.attrs) {
0361         rc = -ENOMEM;
0362         goto fail;
0363     }
0364 
0365     for (i = 0, j = -1, type = -1; ; i++) {
0366         enum efx_hwmon_type hwmon_type;
0367         const char *hwmon_prefix;
0368         unsigned hwmon_index;
0369         u16 min1, max1, min2, max2;
0370 
0371         /* Find next sensor type or exit if there is none */
0372         do {
0373             type++;
0374 
0375             if ((type % 32) == 0) {
0376                 page = type / 32;
0377                 j = -1;
0378                 if (page == n_pages)
0379                     goto hwmon_register;
0380 
0381                 MCDI_SET_DWORD(inbuf, SENSOR_INFO_EXT_IN_PAGE,
0382                            page);
0383                 rc = efx_mcdi_rpc(efx, MC_CMD_SENSOR_INFO,
0384                           inbuf, sizeof(inbuf),
0385                           outbuf, sizeof(outbuf),
0386                           &outlen);
0387                 if (rc)
0388                     goto fail;
0389                 if (outlen < MC_CMD_SENSOR_INFO_OUT_LENMIN) {
0390                     rc = -EIO;
0391                     goto fail;
0392                 }
0393 
0394                 mask = (MCDI_DWORD(outbuf,
0395                            SENSOR_INFO_OUT_MASK) &
0396                     ~(1 << MC_CMD_SENSOR_PAGE0_NEXT));
0397 
0398                 /* Check again for short response */
0399                 if (outlen <
0400                     MC_CMD_SENSOR_INFO_OUT_LEN(hweight32(mask))) {
0401                     rc = -EIO;
0402                     goto fail;
0403                 }
0404             }
0405         } while (!(mask & (1 << type % 32)));
0406         j++;
0407 
0408         if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) {
0409             hwmon_type = efx_mcdi_sensor_type[type].hwmon_type;
0410 
0411             /* Skip sensors specific to a different port */
0412             if (hwmon_type != EFX_HWMON_UNKNOWN &&
0413                 efx_mcdi_sensor_type[type].port >= 0 &&
0414                 efx_mcdi_sensor_type[type].port !=
0415                 efx_port_num(efx))
0416                 continue;
0417         } else {
0418             hwmon_type = EFX_HWMON_UNKNOWN;
0419         }
0420 
0421         switch (hwmon_type) {
0422         case EFX_HWMON_TEMP:
0423             hwmon_prefix = "temp";
0424             hwmon_index = ++n_temp; /* 1-based */
0425             break;
0426         case EFX_HWMON_COOL:
0427             /* This is likely to be a heatsink, but there
0428              * is no convention for representing cooling
0429              * devices other than fans.
0430              */
0431             hwmon_prefix = "fan";
0432             hwmon_index = ++n_cool; /* 1-based */
0433             break;
0434         default:
0435             hwmon_prefix = "in";
0436             hwmon_index = n_in++; /* 0-based */
0437             break;
0438         case EFX_HWMON_CURR:
0439             hwmon_prefix = "curr";
0440             hwmon_index = ++n_curr; /* 1-based */
0441             break;
0442         case EFX_HWMON_POWER:
0443             hwmon_prefix = "power";
0444             hwmon_index = ++n_power; /* 1-based */
0445             break;
0446         }
0447 
0448         min1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
0449                     SENSOR_INFO_ENTRY, j, MIN1);
0450         max1 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
0451                     SENSOR_INFO_ENTRY, j, MAX1);
0452         min2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
0453                     SENSOR_INFO_ENTRY, j, MIN2);
0454         max2 = MCDI_ARRAY_FIELD(outbuf, SENSOR_ENTRY,
0455                     SENSOR_INFO_ENTRY, j, MAX2);
0456 
0457         if (min1 != max1) {
0458             snprintf(name, sizeof(name), "%s%u_input",
0459                  hwmon_prefix, hwmon_index);
0460             efx_mcdi_mon_add_attr(
0461                 efx, name, efx_mcdi_mon_show_value, i, type, 0);
0462 
0463             if (hwmon_type != EFX_HWMON_POWER) {
0464                 snprintf(name, sizeof(name), "%s%u_min",
0465                      hwmon_prefix, hwmon_index);
0466                 efx_mcdi_mon_add_attr(
0467                     efx, name, efx_mcdi_mon_show_limit,
0468                     i, type, min1);
0469             }
0470 
0471             snprintf(name, sizeof(name), "%s%u_max",
0472                  hwmon_prefix, hwmon_index);
0473             efx_mcdi_mon_add_attr(
0474                 efx, name, efx_mcdi_mon_show_limit,
0475                 i, type, max1);
0476 
0477             if (min2 != max2) {
0478                 /* Assume max2 is critical value.
0479                  * But we have no good way to expose min2.
0480                  */
0481                 snprintf(name, sizeof(name), "%s%u_crit",
0482                      hwmon_prefix, hwmon_index);
0483                 efx_mcdi_mon_add_attr(
0484                     efx, name, efx_mcdi_mon_show_limit,
0485                     i, type, max2);
0486             }
0487         }
0488 
0489         snprintf(name, sizeof(name), "%s%u_alarm",
0490              hwmon_prefix, hwmon_index);
0491         efx_mcdi_mon_add_attr(
0492             efx, name, efx_mcdi_mon_show_alarm, i, type, 0);
0493 
0494         if (type < ARRAY_SIZE(efx_mcdi_sensor_type) &&
0495             efx_mcdi_sensor_type[type].label) {
0496             snprintf(name, sizeof(name), "%s%u_label",
0497                  hwmon_prefix, hwmon_index);
0498             efx_mcdi_mon_add_attr(
0499                 efx, name, efx_mcdi_mon_show_label, i, type, 0);
0500         }
0501     }
0502 
0503 hwmon_register:
0504     hwmon->groups[0] = &hwmon->group;
0505     hwmon->device = hwmon_device_register_with_groups(&efx->pci_dev->dev,
0506                               KBUILD_MODNAME, NULL,
0507                               hwmon->groups);
0508     if (IS_ERR(hwmon->device)) {
0509         rc = PTR_ERR(hwmon->device);
0510         goto fail;
0511     }
0512 
0513     return 0;
0514 
0515 fail:
0516     efx_mcdi_mon_remove(efx);
0517     return rc;
0518 }
0519 
0520 void efx_mcdi_mon_remove(struct efx_nic *efx)
0521 {
0522     struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx);
0523 
0524     if (hwmon->device)
0525         hwmon_device_unregister(hwmon->device);
0526     kfree(hwmon->attrs);
0527     kfree(hwmon->group.attrs);
0528     efx_nic_free_buffer(efx, &hwmon->dma_buf);
0529 }
0530 
0531 #endif /* CONFIG_SFC_MCDI_MON */