Commit 51a8f9d7 authored by Alvaro Karsz's avatar Alvaro Karsz Committed by Michael S. Tsirkin
Browse files

virtio: vdpa: new SolidNET DPU driver.



This commit includes:
 1) The driver to manage the controlplane over vDPA bus.
 2) A HW monitor device to read health values from the DPU.

Signed-off-by: default avatarAlvaro Karsz <alvaro.karsz@solid-run.com>
Acked-by: default avatarJason Wang <jasowang@redhat.com>
Message-Id: <20230110165638.123745-4-alvaro.karsz@solid-run.com>
Message-Id: <20230209075128.78915-1-alvaro.karsz@solid-run.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent d089d69c
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -22061,6 +22061,10 @@ IFCVF VIRTIO DATA PATH ACCELERATOR
R:	Zhu Lingshan <lingshan.zhu@intel.com>
F:	drivers/vdpa/ifcvf/
SNET DPU VIRTIO DATA PATH ACCELERATOR
R:	Alvaro Karsz <alvaro.karsz@solid-run.com>
F:	drivers/vdpa/solidrun/
VIRTIO BALLOON
M:	"Michael S. Tsirkin" <mst@redhat.com>
M:	David Hildenbrand <david@redhat.com>
+18 −0
Original line number Diff line number Diff line
@@ -98,4 +98,22 @@ config ALIBABA_ENI_VDPA
	  VDPA driver for Alibaba ENI (Elastic Network Interface) which is built upon
	  virtio 0.9.5 specification.

 config SNET_VDPA
	tristate "SolidRun's vDPA driver for SolidNET"
	depends on PCI_MSI && PCI_IOV && (HWMON || HWMON=n)

	# This driver MAY create a HWMON device.
	# Depending on (HWMON || HWMON=n) ensures that:
	# If HWMON=n the driver can be compiled either as a module or built-in.
	# If HWMON=y the driver can be compiled either as a module or built-in.
	# If HWMON=m the driver is forced to be compiled as a module.
	# By doing so, IS_ENABLED can be used instead of IS_REACHABLE

	help
	  vDPA driver for SolidNET DPU.
	  With this driver, the VirtIO dataplane can be
	  offloaded to a SolidNET DPU.
	  This driver includes a HW monitor device that
	  reads health values from the DPU.

endif # VDPA
+1 −0
Original line number Diff line number Diff line
@@ -6,3 +6,4 @@ obj-$(CONFIG_IFCVF) += ifcvf/
obj-$(CONFIG_MLX5_VDPA) += mlx5/
obj-$(CONFIG_VP_VDPA)    += virtio_pci/
obj-$(CONFIG_ALIBABA_ENI_VDPA) += alibaba/
obj-$(CONFIG_SNET_VDPA) += solidrun/
+6 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SNET_VDPA) += snet_vdpa.o
snet_vdpa-$(CONFIG_SNET_VDPA) += snet_main.o
ifdef CONFIG_HWMON
snet_vdpa-$(CONFIG_SNET_VDPA) += snet_hwmon.o
endif
+188 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * SolidRun DPU driver for control plane
 *
 * Copyright (C) 2022 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));
}
Loading