/* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2023 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. */ #include #include #include #include #include #include "bnxt_hsi.h" #include "bnxt.h" #include "bnxt_hwrm.h" #include "bnxt_hwmon.h" void bnxt_hwmon_notify_event(struct bnxt *bp) { u32 attr; if (!bp->hwmon_dev) return; switch (bp->thermal_threshold_type) { case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_WARN: attr = hwmon_temp_max_alarm; break; case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_CRITICAL: attr = hwmon_temp_crit_alarm; break; case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_FATAL: case ASYNC_EVENT_CMPL_ERROR_REPORT_THERMAL_EVENT_DATA1_THRESHOLD_TYPE_SHUTDOWN: attr = hwmon_temp_emergency_alarm; break; default: return; } hwmon_notify_event(&bp->pdev->dev, hwmon_temp, attr, 0); } static int bnxt_hwrm_temp_query(struct bnxt *bp, u8 *temp) { struct hwrm_temp_monitor_query_output *resp; struct hwrm_temp_monitor_query_input *req; int rc; rc = hwrm_req_init(bp, req, HWRM_TEMP_MONITOR_QUERY); if (rc) return rc; resp = hwrm_req_hold(bp, req); rc = hwrm_req_send_silent(bp, req); if (rc) goto drop_req; if (temp) { *temp = resp->temp; } else if (resp->flags & TEMP_MONITOR_QUERY_RESP_FLAGS_THRESHOLD_VALUES_AVAILABLE) { bp->fw_cap |= BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED; bp->warn_thresh_temp = resp->warn_threshold; bp->crit_thresh_temp = resp->critical_threshold; bp->fatal_thresh_temp = resp->fatal_threshold; bp->shutdown_thresh_temp = resp->shutdown_threshold; } drop_req: hwrm_req_drop(bp, req); return rc; } static umode_t bnxt_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, int channel) { const struct bnxt *bp = _data; if (type != hwmon_temp) return 0; switch (attr) { case hwmon_temp_input: return 0444; case hwmon_temp_max: case hwmon_temp_crit: case hwmon_temp_emergency: case hwmon_temp_max_alarm: case hwmon_temp_crit_alarm: case hwmon_temp_emergency_alarm: if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED)) return 0; return 0444; default: return 0; } } static int bnxt_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct bnxt *bp = dev_get_drvdata(dev); u8 temp = 0; int rc; switch (attr) { case hwmon_temp_input: rc = bnxt_hwrm_temp_query(bp, &temp); if (!rc) *val = temp * 1000; return rc; case hwmon_temp_max: *val = bp->warn_thresh_temp * 1000; return 0; case hwmon_temp_crit: *val = bp->crit_thresh_temp * 1000; return 0; case hwmon_temp_emergency: *val = bp->fatal_thresh_temp * 1000; return 0; case hwmon_temp_max_alarm: rc = bnxt_hwrm_temp_query(bp, &temp); if (!rc) *val = temp >= bp->warn_thresh_temp; return rc; case hwmon_temp_crit_alarm: rc = bnxt_hwrm_temp_query(bp, &temp); if (!rc) *val = temp >= bp->crit_thresh_temp; return rc; case hwmon_temp_emergency_alarm: rc = bnxt_hwrm_temp_query(bp, &temp); if (!rc) *val = temp >= bp->fatal_thresh_temp; return rc; default: return -EOPNOTSUPP; } } static const struct hwmon_channel_info *bnxt_hwmon_info[] = { HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM), NULL }; static const struct hwmon_ops bnxt_hwmon_ops = { .is_visible = bnxt_hwmon_is_visible, .read = bnxt_hwmon_read, }; static const struct hwmon_chip_info bnxt_hwmon_chip_info = { .ops = &bnxt_hwmon_ops, .info = bnxt_hwmon_info, }; static ssize_t temp1_shutdown_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bnxt *bp = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", bp->shutdown_thresh_temp * 1000); } static ssize_t temp1_shutdown_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bnxt *bp = dev_get_drvdata(dev); u8 temp; int rc; rc = bnxt_hwrm_temp_query(bp, &temp); if (rc) return -EIO; return sysfs_emit(buf, "%u\n", temp >= bp->shutdown_thresh_temp); } static DEVICE_ATTR_RO(temp1_shutdown); static DEVICE_ATTR_RO(temp1_shutdown_alarm); static struct attribute *bnxt_temp_extra_attrs[] = { &dev_attr_temp1_shutdown.attr, &dev_attr_temp1_shutdown_alarm.attr, NULL, }; static umode_t bnxt_temp_extra_attrs_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = kobj_to_dev(kobj); struct bnxt *bp = dev_get_drvdata(dev); /* Shutdown temperature setting in NVM is optional */ if (!(bp->fw_cap & BNXT_FW_CAP_THRESHOLD_TEMP_SUPPORTED) || !bp->shutdown_thresh_temp) return 0; return attr->mode; } static const struct attribute_group bnxt_temp_extra_group = { .attrs = bnxt_temp_extra_attrs, .is_visible = bnxt_temp_extra_attrs_visible, }; __ATTRIBUTE_GROUPS(bnxt_temp_extra); void bnxt_hwmon_uninit(struct bnxt *bp) { if (bp->hwmon_dev) { hwmon_device_unregister(bp->hwmon_dev); bp->hwmon_dev = NULL; } } void bnxt_hwmon_init(struct bnxt *bp) { struct pci_dev *pdev = bp->pdev; int rc; /* temp1_xxx is only sensor, ensure not registered if it will fail */ rc = bnxt_hwrm_temp_query(bp, NULL); if (rc == -EACCES || rc == -EOPNOTSUPP) { bnxt_hwmon_uninit(bp); return; } if (bp->hwmon_dev) return; bp->hwmon_dev = hwmon_device_register_with_info(&pdev->dev, DRV_MODULE_NAME, bp, &bnxt_hwmon_chip_info, bnxt_temp_extra_groups); if (IS_ERR(bp->hwmon_dev)) { bp->hwmon_dev = NULL; dev_warn(&pdev->dev, "Cannot register hwmon device\n"); } }