[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20211230163909.160269-6-dmichail@fungible.com>
Date: Thu, 30 Dec 2021 08:39:06 -0800
From: Dimitris Michailidis <d.michailidis@...gible.com>
To: davem@...emloft.net, kuba@...nel.org, netdev@...r.kernel.org
Subject: [PATCH net-next 5/8] net/funeth: devlink support
The devlink part, primarily FW updating.
Signed-off-by: Dimitris Michailidis <dmichail@...gible.com>
---
.../ethernet/fungible/funeth/funeth_devlink.c | 273 ++++++++++++++++++
.../ethernet/fungible/funeth/funeth_devlink.h | 13 +
2 files changed, 286 insertions(+)
create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_devlink.c
create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_devlink.h
diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c
new file mode 100644
index 000000000000..834648b131f8
--- /dev/null
+++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+
+#include <linux/firmware.h>
+#include <linux/pci.h>
+
+#include "funeth.h"
+#include "funeth_devlink.h"
+
+/* max length of scatter-gather list used to pass FW image data to the device */
+#define FUN_FW_SGL_LEN ((ADMIN_SQE_SIZE - sizeof(struct fun_admin_swu_req)) / \
+ sizeof(struct fun_subop_sgl))
+
+/* size of each DMA buffer that is part of the above SGL */
+#define FUN_FW_SGL_BUF_LEN 65536U
+
+struct fun_fw_buf {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ unsigned int data_len;
+};
+
+/* Start or commit the FW update for the given component with a FW image of
+ * size @img_size.
+ */
+static int fun_fw_update_one(struct fun_dev *fdev, unsigned int handle,
+ unsigned int comp_id, unsigned int flags,
+ unsigned int img_size)
+{
+ union {
+ struct fun_admin_swu_req req;
+ struct fun_admin_swu_rsp rsp;
+ } cmd;
+ int rc;
+
+ cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_SWUPGRADE,
+ sizeof(cmd.req));
+ cmd.req.u.upgrade =
+ FUN_ADMIN_SWU_UPGRADE_REQ_INIT(FUN_ADMIN_SWU_SUBOP_UPGRADE,
+ flags, handle, comp_id,
+ img_size);
+ rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, &cmd.rsp,
+ sizeof(cmd.rsp), 0);
+ if (!rc)
+ rc = -be32_to_cpu(cmd.rsp.u.upgrade.status);
+ return rc;
+}
+
+/* DMA a gather list of FW image data starting at @offset to the device's FW
+ * staging area.
+ */
+static int fun_fw_write(struct fun_dev *fdev, unsigned int handle,
+ unsigned int offset, unsigned int nsgl,
+ const struct fun_fw_buf *bufs)
+{
+ unsigned int cmd_sz, total_data_len = 0, i;
+ union {
+ struct fun_admin_swu_req req;
+ u8 v[ADMIN_SQE_SIZE];
+ } cmd;
+
+ cmd_sz = struct_size(&cmd.req, sgl, nsgl);
+ if (cmd_sz > sizeof(cmd))
+ return -EINVAL;
+
+ cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_SWUPGRADE,
+ cmd_sz);
+
+ for (i = 0; i < nsgl; i++, bufs++) {
+ total_data_len += bufs->data_len;
+ cmd.req.sgl[i] = FUN_SUBOP_SGL_INIT(FUN_DATAOP_GL, 0,
+ i ? 0 : nsgl,
+ bufs->data_len,
+ bufs->dma_addr);
+ }
+
+ cmd.req.u.upgrade_data =
+ FUN_ADMIN_SWU_UPGRADE_DATA_REQ_INIT(FUN_ADMIN_SWU_SUBOP_UPGRADE_DATA,
+ 0, handle, offset,
+ total_data_len);
+ return fun_submit_admin_sync_cmd(fdev, &cmd.req.common, NULL, 0, 0);
+}
+
+/* Convert a FW component string into a component ID.
+ * Component names are 4 characters long.
+ */
+static unsigned int fw_component_id(const char *component)
+{
+ if (strlen(component) != 4)
+ return 0;
+
+ return component[0] | (component[1] << 8) | (component[2] << 16) |
+ (component[3] << 24);
+}
+
+/* Allocate the SG buffers for the DMA transfer of the FW image of the
+ * given size. We allocate up to the max SG length supported by the device.
+ * Return success if at least 1 buffer is allocated.
+ */
+static int fun_init_fw_dma_bufs(struct device *dev, struct fun_fw_buf *bufs,
+ size_t fw_size)
+{
+ unsigned int i, nbufs;
+
+ nbufs = min(FUN_FW_SGL_LEN, DIV_ROUND_UP(fw_size, FUN_FW_SGL_BUF_LEN));
+ for (i = 0; i < nbufs; i++, bufs++) {
+ bufs->vaddr = dma_alloc_coherent(dev, FUN_FW_SGL_BUF_LEN,
+ &bufs->dma_addr, GFP_KERNEL);
+ if (!bufs->vaddr) {
+ if (!i)
+ return -ENOMEM;
+ break;
+ }
+ }
+ return i;
+}
+
+static void fun_free_fw_bufs(struct device *dev, struct fun_fw_buf *bufs,
+ unsigned int nbufs)
+{
+ for ( ; nbufs; nbufs--, bufs++)
+ dma_free_coherent(dev, FUN_FW_SGL_BUF_LEN, bufs->vaddr,
+ bufs->dma_addr);
+}
+
+/* Scatter the FW data starting at @offset into the @nbufs DMA buffers.
+ * Return the new offset into the FW image.
+ */
+static unsigned int fun_fw_scatter(struct fun_fw_buf *bufs, unsigned int nbufs,
+ const struct firmware *fw,
+ unsigned int offset)
+{
+ for ( ; nbufs && offset < fw->size; nbufs--, bufs++) {
+ bufs->data_len = min_t(unsigned int, fw->size - offset,
+ FUN_FW_SGL_BUF_LEN);
+ memcpy(bufs->vaddr, fw->data + offset, bufs->data_len);
+ offset += bufs->data_len;
+ }
+ return offset;
+}
+
+static int fun_dl_flash_update(struct devlink *devlink,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack)
+{
+ unsigned int comp_id, update_flags, nbufs, offset;
+ struct fun_dev *fdev = devlink_priv(devlink);
+ const char *component = params->component;
+ struct fun_fw_buf bufs[FUN_FW_SGL_LEN];
+ const struct firmware *fw;
+ int err;
+
+ if (!to_pci_dev(fdev->dev)->is_physfn)
+ return -EOPNOTSUPP;
+
+ if (!component) {
+ NL_SET_ERR_MSG_MOD(extack, "must specify FW component");
+ return -EINVAL;
+ }
+
+ comp_id = fw_component_id(component);
+ if (!comp_id) {
+ NL_SET_ERR_MSG_MOD(extack, "bad FW component");
+ return -EINVAL;
+ }
+
+ err = fun_get_fw_handle(fdev);
+ if (err < 0) {
+ NL_SET_ERR_MSG_MOD(extack, "can't create FW update handle");
+ return err;
+ }
+
+ fw = params->fw;
+
+ err = fun_init_fw_dma_bufs(fdev->dev, bufs, fw->size);
+ if (err < 0) {
+ NL_SET_ERR_MSG_MOD(extack, "unable to create FW DMA SGL");
+ return err;
+ }
+ nbufs = err;
+
+ devlink_flash_update_status_notify(devlink, "Preparing to flash",
+ component, 0, 1);
+
+ err = fun_fw_update_one(fdev, fdev->fw_handle, comp_id,
+ FUN_ADMIN_SWU_UPGRADE_FLAG_INIT, fw->size);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "unable to create device staging area for FW image");
+ goto free_bufs;
+ }
+
+ devlink_flash_update_status_notify(devlink, "Preparing to flash",
+ component, 1, 1);
+
+ /* Write FW to the device staging area, in chunks if needed. */
+ for (offset = 0; offset < fw->size; ) {
+ unsigned int new_offset, nsgl;
+
+ new_offset = fun_fw_scatter(bufs, nbufs, fw, offset);
+ nsgl = DIV_ROUND_UP(new_offset - offset, FUN_FW_SGL_BUF_LEN);
+ devlink_flash_update_status_notify(devlink, "Staging FW",
+ component, offset, fw->size);
+ err = fun_fw_write(fdev, fdev->fw_handle, offset, nsgl, bufs);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "error staging FW image");
+ goto free_bufs;
+ }
+ offset = new_offset;
+ }
+ devlink_flash_update_status_notify(devlink, "Staging FW",
+ component, offset, fw->size);
+
+ update_flags = FUN_ADMIN_SWU_UPGRADE_FLAG_COMPLETE |
+ FUN_ADMIN_SWU_UPGRADE_FLAG_DOWNGRADE;
+ err = fun_fw_update_one(fdev, fdev->fw_handle, comp_id, update_flags,
+ fw->size);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "unable to commit FW update");
+ devlink_flash_update_status_notify(devlink, "FW update failed",
+ component, 0, fw->size);
+ } else {
+ devlink_flash_update_status_notify(devlink, "FW updated",
+ component, fw->size,
+ fw->size);
+ }
+
+free_bufs:
+ fun_free_fw_bufs(fdev->dev, bufs, nbufs);
+ return err;
+}
+
+static int fun_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
+ if (err)
+ return err;
+
+ err = devlink_info_version_fixed_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_BOARD_MANUFACTURE,
+ "Fungible");
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static const struct devlink_ops fun_dl_ops = {
+ .info_get = fun_dl_info_get,
+ .flash_update = fun_dl_flash_update,
+};
+
+struct devlink *fun_devlink_alloc(struct device *dev)
+{
+ return devlink_alloc(&fun_dl_ops, sizeof(struct fun_ethdev), dev);
+}
+
+void fun_devlink_free(struct devlink *devlink)
+{
+ devlink_free(devlink);
+}
+
+void fun_devlink_register(struct devlink *devlink)
+{
+ devlink_register(devlink);
+}
+
+void fun_devlink_unregister(struct devlink *devlink)
+{
+ devlink_unregister(devlink);
+}
diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.h b/drivers/net/ethernet/fungible/funeth/funeth_devlink.h
new file mode 100644
index 000000000000..e40464d57ff4
--- /dev/null
+++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef __FUNETH_DEVLINK_H
+#define __FUNETH_DEVLINK_H
+
+#include <net/devlink.h>
+
+struct devlink *fun_devlink_alloc(struct device *dev);
+void fun_devlink_free(struct devlink *devlink);
+void fun_devlink_register(struct devlink *devlink);
+void fun_devlink_unregister(struct devlink *devlink);
+
+#endif /* __FUNETH_DEVLINK_H */
--
2.25.1
Powered by blists - more mailing lists