3f3a1675b7
This patch adds the get_vq_state and set_vq_state vDPA callbacks. In order to get the VQ state, the state needs to be read from the DPU. In order to allow that, the old messaging mechanism is replaced with a new, flexible control mechanism. This mechanism allows to read data from the DPU. The mechanism can be used if the negotiated config version is 2 or higher. If the new mechanism is used when the config version is 1, it will call snet_send_ctrl_msg_old, which is config 1 compatible. Signed-off-by: Alvaro Karsz <alvaro.karsz@solid-run.com> Message-Id: <20230413073337.31367-2-alvaro.karsz@solid-run.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Jason Wang <jasowang@redhat.com>
189 lines
4.4 KiB
C
189 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* SolidRun DPU driver for control plane
|
|
*
|
|
* Copyright (C) 2022-2023 SolidRun
|
|
*
|
|
* Author: Alvaro Karsz <alvaro.karsz@solid-run.com>
|
|
*
|
|
*/
|
|
#include <linux/hwmon.h>
|
|
|
|
#include "snet_vdpa.h"
|
|
|
|
/* Monitor offsets */
|
|
#define SNET_MON_TMP0_IN_OFF 0x00
|
|
#define SNET_MON_TMP0_MAX_OFF 0x08
|
|
#define SNET_MON_TMP0_CRIT_OFF 0x10
|
|
#define SNET_MON_TMP1_IN_OFF 0x18
|
|
#define SNET_MON_TMP1_CRIT_OFF 0x20
|
|
#define SNET_MON_CURR_IN_OFF 0x28
|
|
#define SNET_MON_CURR_MAX_OFF 0x30
|
|
#define SNET_MON_CURR_CRIT_OFF 0x38
|
|
#define SNET_MON_PWR_IN_OFF 0x40
|
|
#define SNET_MON_VOLT_IN_OFF 0x48
|
|
#define SNET_MON_VOLT_CRIT_OFF 0x50
|
|
#define SNET_MON_VOLT_LCRIT_OFF 0x58
|
|
|
|
static void snet_hwmon_read_reg(struct psnet *psnet, u32 reg, long *out)
|
|
{
|
|
*out = psnet_read64(psnet, psnet->cfg.hwmon_off + reg);
|
|
}
|
|
|
|
static umode_t snet_howmon_is_visible(const void *data,
|
|
enum hwmon_sensor_types type,
|
|
u32 attr, int channel)
|
|
{
|
|
return 0444;
|
|
}
|
|
|
|
static int snet_howmon_read(struct device *dev, enum hwmon_sensor_types type,
|
|
u32 attr, int channel, long *val)
|
|
{
|
|
struct psnet *psnet = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
switch (type) {
|
|
case hwmon_in:
|
|
switch (attr) {
|
|
case hwmon_in_lcrit:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_VOLT_LCRIT_OFF, val);
|
|
break;
|
|
case hwmon_in_crit:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_VOLT_CRIT_OFF, val);
|
|
break;
|
|
case hwmon_in_input:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_VOLT_IN_OFF, val);
|
|
break;
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case hwmon_power:
|
|
switch (attr) {
|
|
case hwmon_power_input:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_PWR_IN_OFF, val);
|
|
break;
|
|
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case hwmon_curr:
|
|
switch (attr) {
|
|
case hwmon_curr_input:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_CURR_IN_OFF, val);
|
|
break;
|
|
case hwmon_curr_max:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_CURR_MAX_OFF, val);
|
|
break;
|
|
case hwmon_curr_crit:
|
|
snet_hwmon_read_reg(psnet, SNET_MON_CURR_CRIT_OFF, val);
|
|
break;
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case hwmon_temp:
|
|
switch (attr) {
|
|
case hwmon_temp_input:
|
|
if (channel == 0)
|
|
snet_hwmon_read_reg(psnet, SNET_MON_TMP0_IN_OFF, val);
|
|
else
|
|
snet_hwmon_read_reg(psnet, SNET_MON_TMP1_IN_OFF, val);
|
|
break;
|
|
case hwmon_temp_max:
|
|
if (channel == 0)
|
|
snet_hwmon_read_reg(psnet, SNET_MON_TMP0_MAX_OFF, val);
|
|
else
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
case hwmon_temp_crit:
|
|
if (channel == 0)
|
|
snet_hwmon_read_reg(psnet, SNET_MON_TMP0_CRIT_OFF, val);
|
|
else
|
|
snet_hwmon_read_reg(psnet, SNET_MON_TMP1_CRIT_OFF, val);
|
|
break;
|
|
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int snet_hwmon_read_string(struct device *dev,
|
|
enum hwmon_sensor_types type, u32 attr,
|
|
int channel, const char **str)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (type) {
|
|
case hwmon_in:
|
|
*str = "main_vin";
|
|
break;
|
|
case hwmon_power:
|
|
*str = "soc_pin";
|
|
break;
|
|
case hwmon_curr:
|
|
*str = "soc_iin";
|
|
break;
|
|
case hwmon_temp:
|
|
if (channel == 0)
|
|
*str = "power_stage_temp";
|
|
else
|
|
*str = "ic_junction_temp";
|
|
break;
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const struct hwmon_ops snet_hwmon_ops = {
|
|
.is_visible = snet_howmon_is_visible,
|
|
.read = snet_howmon_read,
|
|
.read_string = snet_hwmon_read_string
|
|
};
|
|
|
|
static const struct hwmon_channel_info *snet_hwmon_info[] = {
|
|
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_LABEL,
|
|
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL),
|
|
HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL),
|
|
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | HWMON_C_LABEL),
|
|
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_LCRIT | HWMON_I_LABEL),
|
|
NULL
|
|
};
|
|
|
|
static const struct hwmon_chip_info snet_hwmono_info = {
|
|
.ops = &snet_hwmon_ops,
|
|
.info = snet_hwmon_info,
|
|
};
|
|
|
|
/* Create an HW monitor device */
|
|
void psnet_create_hwmon(struct pci_dev *pdev)
|
|
{
|
|
struct device *hwmon;
|
|
struct psnet *psnet = pci_get_drvdata(pdev);
|
|
|
|
snprintf(psnet->hwmon_name, SNET_NAME_SIZE, "snet_%s", pci_name(pdev));
|
|
hwmon = devm_hwmon_device_register_with_info(&pdev->dev, psnet->hwmon_name, psnet,
|
|
&snet_hwmono_info, NULL);
|
|
/* The monitor is not mandatory, Just alert user in case of an error */
|
|
if (IS_ERR(hwmon))
|
|
SNET_WARN(pdev, "Failed to create SNET hwmon, error %ld\n", PTR_ERR(hwmon));
|
|
}
|