Initial files for adding a new fwctl driver for the AMD/Pensando PDS
devices. This sets up a simple auxiliary_bus driver that registers
with fwctl subsystem. It expects that a pds_core device has set up
the auxiliary_device pds_core.fwctl
Signed-off-by: Shannon Nelson <shannon.nelson@xxxxxxx>
---
MAINTAINERS | 7 ++
drivers/fwctl/Kconfig | 10 ++
drivers/fwctl/Makefile | 1 +
drivers/fwctl/pds/Makefile | 4 +
drivers/fwctl/pds/main.c | 195 +++++++++++++++++++++++++++++++++
include/linux/pds/pds_adminq.h | 77 +++++++++++++
include/uapi/fwctl/fwctl.h | 1 +
include/uapi/fwctl/pds.h | 27 +++++
8 files changed, 322 insertions(+)
create mode 100644 drivers/fwctl/pds/Makefile
create mode 100644 drivers/fwctl/pds/main.c
create mode 100644 include/uapi/fwctl/pds.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 413ab79bf2f4..123f8a9c0b26 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9602,6 +9602,13 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml
F: drivers/media/i2c/gc2145.c
+FWCTL PDS DRIVER
+M: Brett Creeley <brett.creeley@xxxxxxx>
+R: Shannon Nelson <shannon.nelson@xxxxxxx>
+L: linux-kernel@xxxxxxxxxxxxxxx
+S: Maintained
+F: drivers/fwctl/pds/
+
GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER
M: Tim Harvey <tharvey@xxxxxxxxxxxxx>
S: Maintained
diff --git a/drivers/fwctl/Kconfig b/drivers/fwctl/Kconfig
index 0a542a247303..df87ce5bd8aa 100644
--- a/drivers/fwctl/Kconfig
+++ b/drivers/fwctl/Kconfig
@@ -28,5 +28,15 @@ config FWCTL_MLX5
This will allow configuration and debug tools to work out of the box on
mainstream kernel.
+ If you don't know what to do here, say N.
+
+config FWCTL_PDS
+ tristate "AMD/Pensando pds fwctl driver"
+ depends on PDS_CORE
+ help
+ The pds_fwctl driver provides an fwctl interface for a user process
+ to access the debug and configuration information of the AMD/Pensando
+ DSC hardware family.
+
If you don't know what to do here, say N.
endif
diff --git a/drivers/fwctl/Makefile b/drivers/fwctl/Makefile
index 5fb289243286..692e4b8d7beb 100644
--- a/drivers/fwctl/Makefile
+++ b/drivers/fwctl/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_FWCTL) += fwctl.o
obj-$(CONFIG_FWCTL_BNXT) += bnxt/
obj-$(CONFIG_FWCTL_MLX5) += mlx5/
+obj-$(CONFIG_FWCTL_PDS) += pds/
fwctl-y += main.o
diff --git a/drivers/fwctl/pds/Makefile b/drivers/fwctl/pds/Makefile
new file mode 100644
index 000000000000..c14cba128e3b
--- /dev/null
+++ b/drivers/fwctl/pds/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+obj-$(CONFIG_FWCTL_PDS) += pds_fwctl.o
+
+pds_fwctl-y += main.o
diff --git a/drivers/fwctl/pds/main.c b/drivers/fwctl/pds/main.c
new file mode 100644
index 000000000000..24979fe0deea
--- /dev/null
+++ b/drivers/fwctl/pds/main.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) Advanced Micro Devices, Inc */
+
+#include <linux/module.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+
+#include <uapi/fwctl/fwctl.h>
+#include <uapi/fwctl/pds.h>
+#include <linux/fwctl.h>
+
+#include <linux/pds/pds_common.h>
+#include <linux/pds/pds_core_if.h>
+#include <linux/pds/pds_adminq.h>
+#include <linux/pds/pds_auxbus.h>
+
+struct pdsfc_uctx {
+ struct fwctl_uctx uctx;
+ u32 uctx_caps;
+ u32 uctx_uid;
+};
+
+struct pdsfc_dev {
+ struct fwctl_device fwctl;
+ struct pds_auxiliary_dev *padev;
+ struct pdsc *pdsc;
+ u32 caps;
+ dma_addr_t ident_pa;
+ struct pds_fwctl_ident *ident;
+};
+DEFINE_FREE(pdsfc_dev, struct pdsfc_dev *, if (_T) fwctl_put(&_T->fwctl));
+
+static int pdsfc_open_uctx(struct fwctl_uctx *uctx)
+{
+ struct pdsfc_dev *pdsfc = container_of(uctx->fwctl, struct pdsfc_dev, fwctl);
+ struct pdsfc_uctx *pdsfc_uctx = container_of(uctx, struct pdsfc_uctx, uctx);
+ struct device *dev = &uctx->fwctl->dev;
+
+ dev_dbg(dev, "%s: caps = 0x%04x\n", __func__, pdsfc->caps);
+ pdsfc_uctx->uctx_caps = pdsfc->caps;
+
+ return 0;
+}
+
+static void pdsfc_close_uctx(struct fwctl_uctx *uctx)
+{
+}
+
+static void *pdsfc_info(struct fwctl_uctx *uctx, size_t *length)
+{
+ struct pdsfc_uctx *pdsfc_uctx = container_of(uctx, struct pdsfc_uctx, uctx);
+ struct fwctl_info_pds *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->uctx_caps = pdsfc_uctx->uctx_caps;
+
+ return info;
+}
+
+static void pdsfc_free_ident(struct pdsfc_dev *pdsfc)
+{
+ struct device *dev = &pdsfc->fwctl.dev;
+
+ if (pdsfc->ident) {
+ dma_free_coherent(dev, sizeof(*pdsfc->ident),
+ pdsfc->ident, pdsfc->ident_pa);
+ pdsfc->ident = NULL;
+ pdsfc->ident_pa = DMA_MAPPING_ERROR;
+ }
+}
+
+static int pdsfc_identify(struct pdsfc_dev *pdsfc)
+{
+ struct device *dev = &pdsfc->fwctl.dev;
+ union pds_core_adminq_comp comp = {0};
+ union pds_core_adminq_cmd cmd = {0};
+ struct pds_fwctl_ident *ident;
+ dma_addr_t ident_pa;
+ int err = 0;
+
+ ident = dma_alloc_coherent(dev->parent, sizeof(*ident), &ident_pa, GFP_KERNEL);
+ err = dma_mapping_error(dev->parent, ident_pa);
+ if (err) {
+ dev_err(dev, "Failed to map ident\n");
+ return err;
+ }
+
+ cmd.fwctl_ident.opcode = PDS_FWCTL_CMD_IDENT;
+ cmd.fwctl_ident.version = 0;
+ cmd.fwctl_ident.len = cpu_to_le32(sizeof(*ident));
+ cmd.fwctl_ident.ident_pa = cpu_to_le64(ident_pa);
+
+ err = pds_client_adminq_cmd(pdsfc->padev, &cmd, sizeof(cmd), &comp, 0);
+ if (err) {
+ dma_free_coherent(dev->parent, PAGE_SIZE, ident, ident_pa);
+ dev_err(dev, "Failed to send adminq cmd opcode: %u entity: %u err: %d\n",
+ cmd.fwctl_query.opcode, cmd.fwctl_query.entity, err);
+ return err;
+ }
+
+ pdsfc->ident = ident;
+ pdsfc->ident_pa = ident_pa;
+
+ dev_dbg(dev, "ident: version %u max_req_sz %u max_resp_sz %u max_req_sg_elems %u max_resp_sg_elems %u\n",
+ ident->version, ident->max_req_sz, ident->max_resp_sz,
+ ident->max_req_sg_elems, ident->max_resp_sg_elems);
+
+ return 0;
+}
+
+static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
+ void *in, size_t in_len, size_t *out_len)
+{
+ return NULL;
+}
+
+static const struct fwctl_ops pdsfc_ops = {
+ .device_type = FWCTL_DEVICE_TYPE_PDS,
+ .uctx_size = sizeof(struct pdsfc_uctx),
+ .open_uctx = pdsfc_open_uctx,
+ .close_uctx = pdsfc_close_uctx,
+ .info = pdsfc_info,
+ .fw_rpc = pdsfc_fw_rpc,
+};
+
+static int pdsfc_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct pdsfc_dev *pdsfc __free(pdsfc_dev);
+ struct pds_auxiliary_dev *padev;
+ struct device *dev = &adev->dev;
+ int err = 0;
+
+ padev = container_of(adev, struct pds_auxiliary_dev, aux_dev);
+ pdsfc = fwctl_alloc_device(&padev->vf_pdev->dev, &pdsfc_ops,
+ struct pdsfc_dev, fwctl);