246 lines
5.8 KiB
C
246 lines
5.8 KiB
C
/* 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 <linux/hwmon.h>
|
|
#include <linux/hwmon-sysfs.h>
|
|
|
|
#include "bnxt_compat.h"
|
|
#include "bnxt_hsi.h"
|
|
#include "bnxt.h"
|
|
#include "bnxt_hwrm.h"
|
|
#include "bnxt_hwmon.h"
|
|
|
|
#ifdef CONFIG_BNXT_HWMON
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
#endif
|