get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/122516/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 122516,
    "url": "http://patches.dpdk.org/api/patches/122516/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230125103809.1250080-3-tduszynski@marvell.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230125103809.1250080-3-tduszynski@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230125103809.1250080-3-tduszynski@marvell.com",
    "date": "2023-01-25T10:38:09",
    "name": "[2/2] bus: add platform bus",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "b3a8f793f2f0512fb76b467312260cf3231ee3b6",
    "submitter": {
        "id": 2215,
        "url": "http://patches.dpdk.org/api/people/2215/?format=api",
        "name": "Tomasz Duszynski",
        "email": "tduszynski@marvell.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20230125103809.1250080-3-tduszynski@marvell.com/mbox/",
    "series": [
        {
            "id": 26650,
            "url": "http://patches.dpdk.org/api/series/26650/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=26650",
            "date": "2023-01-25T10:38:08",
            "name": "add platform bus",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/26650/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/122516/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/122516/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 8843A42483;\n\tWed, 25 Jan 2023 11:38:32 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 2108542D74;\n\tWed, 25 Jan 2023 11:38:25 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com\n [67.231.156.173])\n by mails.dpdk.org (Postfix) with ESMTP id 6385F42D66\n for <dev@dpdk.org>; Wed, 25 Jan 2023 11:38:23 +0100 (CET)",
            "from pps.filterd (m0045851.ppops.net [127.0.0.1])\n by mx0b-0016f401.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id\n 30P7tYiT023280; Wed, 25 Jan 2023 02:38:22 -0800",
            "from dc5-exch02.marvell.com ([199.233.59.182])\n by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3nb0f68ncr-2\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Wed, 25 Jan 2023 02:38:22 -0800",
            "from DC5-EXCH02.marvell.com (10.69.176.39) by DC5-EXCH02.marvell.com\n (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.42;\n Wed, 25 Jan 2023 02:38:19 -0800",
            "from maili.marvell.com (10.69.176.80) by DC5-EXCH02.marvell.com\n (10.69.176.39) with Microsoft SMTP Server id 15.0.1497.42 via Frontend\n Transport; Wed, 25 Jan 2023 02:38:19 -0800",
            "from cavium-DT10.. (unknown [10.28.34.39])\n by maili.marvell.com (Postfix) with ESMTP id 34FFC3F706A;\n Wed, 25 Jan 2023 02:38:16 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;\n h=from : to : cc :\n subject : date : message-id : in-reply-to : references : mime-version :\n content-transfer-encoding : content-type; s=pfpt0220;\n bh=qmUt/mcDzUOWgLapG/BYBGra8aw3MJq86fGqD8cvXCw=;\n b=lO56bUNtSKoLbet+UaG0ZFSHDG7BNVkhTaKokq8l64/HpbDHayLI5le/vgNygAPZeuOC\n uYBm53GhSBfSV0X75j84XAxsleUl/S+uLhNHkGS8YDV70m89dg0ai4VkI42GnUyatjLF\n tRWu8JBos2HwFhCJ/ODpvv08x9wNtWw3KCcH/1xjLs1lvi3NN3JpsFV1gLTZ/IoD5vLt\n jh7xpV5du2wnF5LVgZ+6tPPlR8zWcTe5ypTE/tfu58bjZJCij0ZRl8oxQ7ltsSnkNJBR\n 6HF1lIduVt1u9TwLSVmDVTDsm8HhQWE/+gxaTZ/kSSjNeLA2KLEEM5DcLjrZtgnjJrsN YA==",
        "From": "Tomasz Duszynski <tduszynski@marvell.com>",
        "To": "<dev@dpdk.org>, Thomas Monjalon <thomas@monjalon.net>, Tomasz Duszynski\n <tduszynski@marvell.com>",
        "CC": "<jerinj@marvell.com>, <stephen@networkplumber.org>, <chenbo.xia@intel.com>",
        "Subject": "[PATCH 2/2] bus: add platform bus",
        "Date": "Wed, 25 Jan 2023 11:38:09 +0100",
        "Message-ID": "<20230125103809.1250080-3-tduszynski@marvell.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230125103809.1250080-1-tduszynski@marvell.com>",
        "References": "<20230125103809.1250080-1-tduszynski@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Proofpoint-GUID": "hRYyJb2YFet1mCMa-ydBKnerbpy6c8Jc",
        "X-Proofpoint-ORIG-GUID": "hRYyJb2YFet1mCMa-ydBKnerbpy6c8Jc",
        "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.122.1\n definitions=2023-01-25_05,2023-01-25_01,2022-06-22_01",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Platform bus is a software bus under Linux that manages devices which\ngenerally do not have built-in discovery mechanisms. Linux normally\nlearns about platform devices directly from device-tree during\nboot-up phase.\n\nUp to this point, whenever some userspace app needed control over\nplatform device or a range of thereof some sort of driver being\na mixture of vdev/rawdev was required.\n\nIn order to simplify this task, provide an auto-probe\nexperience and separate bus logic from the driver itself,\nadd platform bus support.\n\nCurrently devices backed up by vfio-platform kernel driver\nare supported.\n\nSigned-off-by: Tomasz Duszynski <tduszynski@marvell.com>\n---\n MAINTAINERS                                |   4 +\n doc/guides/rel_notes/release_23_03.rst     |   5 +\n drivers/bus/meson.build                    |   1 +\n drivers/bus/platform/bus_platform_driver.h | 174 ++++++\n drivers/bus/platform/meson.build           |  16 +\n drivers/bus/platform/platform.c            | 604 +++++++++++++++++++++\n drivers/bus/platform/platform_params.c     |  70 +++\n drivers/bus/platform/private.h             |  48 ++\n drivers/bus/platform/version.map           |  10 +\n 9 files changed, 932 insertions(+)\n create mode 100644 drivers/bus/platform/bus_platform_driver.h\n create mode 100644 drivers/bus/platform/meson.build\n create mode 100644 drivers/bus/platform/platform.c\n create mode 100644 drivers/bus/platform/platform_params.c\n create mode 100644 drivers/bus/platform/private.h\n create mode 100644 drivers/bus/platform/version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex 9a0f416d2e..b02666710c 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -581,6 +581,10 @@ VDEV bus driver\n F: drivers/bus/vdev/\n F: app/test/test_vdev.c\n \n+Platform bus driver\n+M: Tomasz Duszynski <tduszynski@marvell.com>\n+F: drivers/bus/platform\n+\n VMBUS bus driver\n M: Long Li <longli@microsoft.com>\n F: drivers/bus/vmbus/\ndiff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst\nindex 84b112a8b1..74b2b1e3ff 100644\n--- a/doc/guides/rel_notes/release_23_03.rst\n+++ b/doc/guides/rel_notes/release_23_03.rst\n@@ -57,6 +57,11 @@ New Features\n \n * **Added multi-process support for axgbe PMD.**\n \n+* **Added platform bus support.**\n+\n+  A platform bus provides a way to use Linux platform devices which\n+  are compatible with vfio-platform kernel driver.\n+\n * **Updated Corigine nfp driver.**\n \n   * Added support for meter options.\ndiff --git a/drivers/bus/meson.build b/drivers/bus/meson.build\nindex 45eab5233d..6d2520c543 100644\n--- a/drivers/bus/meson.build\n+++ b/drivers/bus/meson.build\n@@ -7,6 +7,7 @@ drivers = [\n         'fslmc',\n         'ifpga',\n         'pci',\n+        'platform',\n         'vdev',\n         'vmbus',\n ]\ndiff --git a/drivers/bus/platform/bus_platform_driver.h b/drivers/bus/platform/bus_platform_driver.h\nnew file mode 100644\nindex 0000000000..8291c7f3f6\n--- /dev/null\n+++ b/drivers/bus/platform/bus_platform_driver.h\n@@ -0,0 +1,174 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell.\n+ */\n+\n+#ifndef _BUS_PLATFORM_DRIVER_H_\n+#define _BUS_PLATFORM_DRIVER_H_\n+\n+/**\n+ * @file\n+ * Platform bus interface.\n+ */\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#include <stddef.h>\n+#include <stdint.h>\n+\n+#include <dev_driver.h>\n+#include <rte_common.h>\n+#include <rte_dev.h>\n+#include <rte_os.h>\n+\n+/* Forward declarations */\n+struct rte_platform_bus;\n+struct rte_platform_device;\n+struct rte_platform_driver;\n+\n+/**\n+ * Initialization function for the driver called during platform device probing.\n+ *\n+ * @param pdev\n+ *   Pointer to the platform device.\n+ * @return\n+ *   0 on success, negative value otherwise.\n+ */\n+typedef int (rte_platform_probe_t)(struct rte_platform_device *pdev);\n+\n+/**\n+ * Removal function for the driver called during platform device removal.\n+ *\n+ * @param pdev\n+ *   Pointer to the platform device.\n+ * @return\n+ *   0 on success, negative value otherwise.\n+ */\n+typedef int (rte_platform_remove_t)(struct rte_platform_device *pdev);\n+\n+/**\n+ * Driver specific DMA mapping.\n+ *\n+ * @param pdev\n+ *   Pointer to the platform device.\n+ * @param addr\n+ *   Starting virtual address of memory to be mapped.\n+ * @param iova\n+ *   Starting IOVA address of memory to be mapped.\n+ * @param len\n+ *   Length of memory segment being mapped.\n+ * @return\n+ *   - 0 on success, negative value and rte_errno is set otherwise.\n+ */\n+typedef int (rte_platform_dma_map_t)(struct rte_platform_device *pdev, void *addr, uint64_t iova,\n+\t\t\t\t     size_t len);\n+\n+/**\n+ * Driver specific DMA unmapping.\n+ *\n+ * @param pdev\n+ *   Pointer to the platform device.\n+ * @param addr\n+ *   Starting virtual address of memory to be mapped.\n+ * @param iova\n+ *   Starting IOVA address of memory to be mapped.\n+ * @param len\n+ *   Length of memory segment being mapped.\n+ * @return\n+ *   - 0 on success, negative value and rte_errno is set otherwise.\n+ */\n+typedef int (rte_platform_dma_unmap_t)(struct rte_platform_device *pdev, void *addr, uint64_t iova,\n+\t\t\t\t       size_t len);\n+\n+/**\n+ * A structure describing a platform device resource.\n+ */\n+struct rte_platform_resource {\n+\tchar *name; /**< Resource name specified via reg-names prop in device-tree */\n+\tstruct rte_mem_resource mem; /**< Memory resource */\n+};\n+\n+/**\n+ * A structure describing a platform device.\n+ */\n+struct rte_platform_device {\n+\tRTE_TAILQ_ENTRY(rte_platform_device) next; /**< Next attached platform device */\n+\tstruct rte_device device; /**< Core device */\n+\tstruct rte_platform_driver *driver; /**< Matching device driver */\n+\tchar name[RTE_DEV_NAME_MAX_LEN]; /**< Device name */\n+\tunsigned int num_resource; /**< Number of device resources */\n+\tstruct rte_platform_resource *resource; /**< Device resources */\n+\tint dev_fd; /**< VFIO device fd */\n+};\n+\n+/**\n+ * A structure describing a platform device driver.\n+ */\n+struct rte_platform_driver {\n+\tRTE_TAILQ_ENTRY(rte_platform_driver) next; /**< Next available platform driver */\n+\tstruct rte_driver driver; /**< Core driver */\n+\trte_platform_probe_t *probe;  /**< Device probe function */\n+\trte_platform_remove_t *remove; /**< Device remove function */\n+\trte_platform_dma_map_t *dma_map; /**< Device DMA map function */\n+\trte_platform_dma_unmap_t *dma_unmap; /**< Device DMA unmap function */\n+\tuint32_t drv_flags; /**< Driver flags RTE_PLATFORM_DRV_* */\n+};\n+\n+/** Device driver needs IOVA as VA and cannot work with IOVA as PA */\n+#define RTE_PLATFORM_DRV_NEED_IOVA_AS_VA 0x0001\n+\n+/**\n+ * @internal\n+ * Helper macros used to convert core device to platform device.\n+ */\n+#define RTE_DEV_TO_PLATFORM_DEV(ptr) \\\n+\tcontainer_of(ptr, struct rte_platform_device, device)\n+\n+#define RTE_DEV_TO_PLATFORM_DEV_CONST(ptr) \\\n+\tcontainer_of(ptr, const struct rte_platform_device, device)\n+\n+/**\n+ * Register a platform device driver.\n+ *\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice.\n+ *\n+ * @param pdrv\n+ *   A pointer to a rte_platform_driver structure describing driver to be registered.\n+ */\n+__rte_internal\n+void rte_platform_register(struct rte_platform_driver *pdrv);\n+\n+/** Helper for platform driver registration. */\n+#define RTE_PMD_REGISTER_PLATFORM(nm, platform_drv) \\\n+static const char *pdrvinit_ ## nm ## _alias; \\\n+RTE_INIT(pdrvinitfn_ ##nm) \\\n+{ \\\n+\t(platform_drv).driver.name = RTE_STR(nm); \\\n+\t(platform_drv).driver.alias = pdrvinit_ ## nm ## _alias; \\\n+\trte_platform_register(&(platform_drv)); \\\n+} \\\n+RTE_PMD_EXPORT_NAME(nm, __COUNTER__)\n+\n+/** Helper for setting platform driver alias. */\n+#define RTE_PMD_REGISTER_ALIAS(nm, alias) \\\n+static const char *pdrvinit_ ## nm ## _alias = RTE_STR(alias)\n+\n+/**\n+ * Unregister a platform device driver.\n+ *\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice.\n+ *\n+ * @param pdrv\n+ *   A pointer to a rte_platform_driver structure describing driver to be unregistered.\n+ */\n+__rte_internal\n+void rte_platform_unregister(struct rte_platform_driver *pdrv);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _BUS_PLATFORM_DRIVER_H_ */\ndiff --git a/drivers/bus/platform/meson.build b/drivers/bus/platform/meson.build\nnew file mode 100644\nindex 0000000000..417d7b81f8\n--- /dev/null\n+++ b/drivers/bus/platform/meson.build\n@@ -0,0 +1,16 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2023 Marvell.\n+#\n+\n+if not is_linux\n+    build = false\n+    reason = 'only supported on Linux'\n+    subdir_done()\n+endif\n+\n+deps += ['kvargs']\n+sources = files(\n+        'platform_params.c',\n+        'platform.c',\n+)\n+driver_sdk_headers += files('bus_platform_driver.h')\ndiff --git a/drivers/bus/platform/platform.c b/drivers/bus/platform/platform.c\nnew file mode 100644\nindex 0000000000..b43a5b9153\n--- /dev/null\n+++ b/drivers/bus/platform/platform.c\n@@ -0,0 +1,604 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell.\n+ */\n+\n+#include <dirent.h>\n+#include <inttypes.h>\n+#include <linux/vfio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/ioctl.h>\n+#include <sys/mman.h>\n+#include <sys/queue.h>\n+#include <unistd.h>\n+\n+#include <bus_driver.h>\n+#include <bus_platform_driver.h>\n+#include <eal_filesystem.h>\n+#include <rte_bus.h>\n+#include <rte_devargs.h>\n+#include <rte_errno.h>\n+#include <rte_log.h>\n+#include <rte_memory.h>\n+#include <rte_string_fns.h>\n+#include <rte_vfio.h>\n+\n+#include \"private.h\"\n+\n+#define PLATFORM_BUS_DEVICES_PATH \"/sys/bus/platform/devices\"\n+\n+void\n+rte_platform_register(struct rte_platform_driver *pdrv)\n+{\n+\tTAILQ_INSERT_TAIL(&platform_bus.driver_list, pdrv, next);\n+}\n+\n+void\n+rte_platform_unregister(struct rte_platform_driver *pdrv)\n+{\n+\tTAILQ_REMOVE(&platform_bus.driver_list, pdrv, next);\n+}\n+\n+static struct rte_devargs *\n+dev_devargs(const char *dev_name)\n+{\n+\tstruct rte_devargs *devargs;\n+\n+\tRTE_EAL_DEVARGS_FOREACH(\"platform\", devargs) {\n+\t\tif (!strcmp(devargs->name, dev_name))\n+\t\t\treturn devargs;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static bool\n+dev_allowed(const char *dev_name)\n+{\n+\tstruct rte_devargs *devargs;\n+\n+\tdevargs = dev_devargs(dev_name);\n+\tif (devargs == NULL)\n+\t\treturn true;\n+\n+\tswitch (platform_bus.bus.conf.scan_mode) {\n+\tcase RTE_BUS_SCAN_UNDEFINED:\n+\tcase RTE_BUS_SCAN_ALLOWLIST:\n+\t\tif (devargs->policy == RTE_DEV_ALLOWED)\n+\t\t\treturn true;\n+\t\tbreak;\n+\tcase RTE_BUS_SCAN_BLOCKLIST:\n+\t\tif (devargs->policy == RTE_DEV_BLOCKED)\n+\t\t\treturn false;\n+\t\tbreak;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static int\n+dev_add(const char *dev_name)\n+{\n+\tstruct rte_platform_device *pdev, *tmp;\n+\tchar path[PATH_MAX];\n+\tunsigned long val;\n+\n+\tpdev = calloc(1, sizeof(*pdev));\n+\tif (pdev == NULL)\n+\t\treturn -ENOMEM;\n+\n+\trte_strscpy(pdev->name, dev_name, sizeof(pdev->name));\n+\tpdev->device.name = pdev->name;\n+\tpdev->device.devargs = dev_devargs(dev_name);\n+\tpdev->device.bus = &platform_bus.bus;\n+\tsnprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH \"/%s/numa_node\", dev_name);\n+\tpdev->device.numa_node = eal_parse_sysfs_value(path, &val) ? rte_socket_id() : val;\n+\n+\tFOREACH_DEVICE_ON_PLATFORM_BUS(tmp) {\n+\t\tif (!strcmp(tmp->name, pdev->name)) {\n+\t\t\tPLATFORM_LOG(INFO, \"device %s already added\\n\", pdev->name);\n+\n+\t\t\tif (tmp->device.devargs != pdev->device.devargs)\n+\t\t\t\trte_devargs_remove(pdev->device.devargs);\n+\n+\t\t\tfree(pdev);\n+\t\t}\n+\t}\n+\n+\tTAILQ_INSERT_HEAD(&platform_bus.device_list, pdev, next);\n+\n+\tPLATFORM_LOG(INFO, \"adding device %s to the list\\n\", dev_name);\n+\n+\treturn 0;\n+}\n+\n+static char *\n+dev_kernel_driver_name(const char *dev_name)\n+{\n+\tchar path[PATH_MAX], buf[BUFSIZ] = { };\n+\tchar *kdrv;\n+\tint ret;\n+\n+\tsnprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH \"/%s/driver\", dev_name);\n+\t/* save space for NUL */\n+\tret = readlink(path, buf, sizeof(buf) - 1);\n+\tif (ret <= 0)\n+\t\treturn NULL;\n+\n+\t/* last token is kernel driver name */\n+\tkdrv = strrchr(buf, '/');\n+\tif (kdrv != NULL)\n+\t\treturn strdup(kdrv + 1);\n+\n+\treturn NULL;\n+}\n+\n+static bool\n+dev_is_bound_vfio_platform(const char *dev_name)\n+{\n+\tchar *kdrv;\n+\tint ret;\n+\n+\tkdrv = dev_kernel_driver_name(dev_name);\n+\tif (!kdrv)\n+\t\treturn false;\n+\n+\tret = strcmp(kdrv, \"vfio-platform\");\n+\tfree(kdrv);\n+\n+\treturn ret == 0;\n+}\n+\n+static int\n+platform_bus_scan(void)\n+{\n+\tconst struct dirent *ent;\n+\tconst char *dev_name;\n+\tint ret = 0;\n+\tDIR *dp;\n+\n+\tif ((dp = opendir(PLATFORM_BUS_DEVICES_PATH)) == NULL) {\n+\t\tPLATFORM_LOG(INFO, \"failed to open %s\\n\", PLATFORM_BUS_DEVICES_PATH);\n+\t\treturn -errno;\n+\t}\n+\n+\twhile ((ent = readdir(dp))) {\n+\t\tdev_name = ent->d_name;\n+\t\tif (dev_name[0] == '.')\n+\t\t\tcontinue;\n+\n+\t\tif (!dev_allowed(dev_name))\n+\t\t\tcontinue;\n+\n+\t\tif (!dev_is_bound_vfio_platform(dev_name))\n+\t\t\tcontinue;\n+\n+\t\tret = dev_add(dev_name);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t}\n+\n+\tclosedir(dp);\n+\n+\treturn ret;\n+}\n+\n+static int\n+device_map_resource_offset(struct rte_platform_device *pdev, struct rte_platform_resource *res,\n+\t\t\t   size_t offset)\n+{\n+\tres->mem.addr = mmap(NULL, res->mem.len, PROT_READ | PROT_WRITE, MAP_PRIVATE, pdev->dev_fd,\n+\t\t\t     offset);\n+\tif (res->mem.addr == MAP_FAILED)\n+\t\treturn -errno;\n+\n+\tPLATFORM_LOG(DEBUG, \"adding resource va = %p len = %\"PRIu64\" name = %s\\n\", res->mem.addr,\n+\t\t     res->mem.len, res->name);\n+\n+\treturn 0;\n+}\n+\n+static void\n+device_unmap_resources(struct rte_platform_device *pdev)\n+{\n+\tstruct rte_platform_resource *res;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < pdev->num_resource; i++) {\n+\t\tres = &pdev->resource[i];\n+\t\tmunmap(res->mem.addr, res->mem.len);\n+\t\tfree(res->name);\n+\t}\n+\n+\tfree(pdev->resource);\n+\tpdev->resource = NULL;\n+\tpdev->num_resource = 0;\n+}\n+\n+static char *\n+of_resource_name(const char *dev_name, int index)\n+{\n+\tchar path[PATH_MAX], buf[BUFSIZ] = { };\n+\tint num = 0, ret;\n+\tchar *name;\n+\n+\tsnprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH \"/%s/of_node/reg-names\", dev_name);\n+\tret = eal_parse_sysfs_string(path, buf, sizeof(buf) - 1);\n+\tif (ret)\n+\t\treturn NULL;\n+\n+\tfor (name = buf; name; name += strlen(name) + 1) {\n+\t\tif (num++ != index)\n+\t\t\tcontinue;\n+\t\treturn strdup(name);\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+device_map_resources(struct rte_platform_device *pdev, unsigned int num)\n+{\n+\tstruct rte_platform_resource *res;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tif (num == 0)\n+\t\tPLATFORM_LOG(WARNING, \"device %s has no resources\\n\", pdev->name);\n+\n+\tpdev->resource = calloc(num, sizeof(*pdev->resource));\n+\tif (pdev->resource == NULL)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i < num; i++) {\n+\t\tstruct vfio_region_info reg_info = {\n+\t\t\t.argsz = sizeof(reg_info),\n+\t\t\t.index = i,\n+\t\t};\n+\n+\t\tret = ioctl(pdev->dev_fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);\n+\t\tif (ret) {\n+\t\t\tPLATFORM_LOG(ERR, \"failed to get region info at %d\\n\", i);\n+\t\t\tret = -errno;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tres = &pdev->resource[i];\n+\t\tres->name = of_resource_name(pdev->name, reg_info.index);\n+\t\tres->mem.len = reg_info.size;\n+\t\tret = device_map_resource_offset(pdev, res, reg_info.offset);\n+\t\tif (ret) {\n+\t\t\tPLATFORM_LOG(ERR, \"failed to ioremap resource at %d\\n\", i);\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tpdev->num_resource++;\n+\t}\n+\n+\treturn 0;\n+out:\n+\tdevice_unmap_resources(pdev);\n+\n+\treturn ret;\n+}\n+\n+static void\n+device_cleanup(struct rte_platform_device *pdev)\n+{\n+\tdevice_unmap_resources(pdev);\n+\trte_vfio_release_device(PLATFORM_BUS_DEVICES_PATH, pdev->name, pdev->dev_fd);\n+}\n+\n+static int\n+device_setup(struct rte_platform_device *pdev)\n+{\n+\tstruct vfio_device_info dev_info = { .argsz = sizeof(dev_info), };\n+\tconst char *name = pdev->name;\n+\tint ret;\n+\n+\tret = rte_vfio_setup_device(PLATFORM_BUS_DEVICES_PATH, name, &pdev->dev_fd, &dev_info);\n+\tif (ret) {\n+\t\tPLATFORM_LOG(ERR, \"failed to setup %s\\n\", name);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tif (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) {\n+\t\tPLATFORM_LOG(ERR, \"device not backed by vfio-platform\\n\");\n+\t\tret = -ENOTSUP;\n+\t\tgoto out;\n+\t}\n+\n+\tret = device_map_resources(pdev, dev_info.num_regions);\n+\tif (ret) {\n+\t\tPLATFORM_LOG(ERR, \"failed to setup platform resources\\n\");\n+\t\tgoto out;\n+\t}\n+\n+\treturn 0;\n+out:\n+\tdevice_cleanup(pdev);\n+\n+\treturn ret;\n+}\n+\n+static int\n+driver_call_probe(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)\n+{\n+\tint ret;\n+\n+\tif (rte_dev_is_probed(&pdev->device))\n+\t\treturn -EBUSY;\n+\n+\tif (pdrv->probe) {\n+\t\tpdev->driver = pdrv;\n+\t\tret = pdrv->probe(pdev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tpdev->device.driver = &pdrv->driver;\n+\n+\treturn 0;\n+}\n+\n+static int\n+driver_probe_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)\n+{\n+\tenum rte_iova_mode iova_mode;\n+\tint ret;\n+\n+\tiova_mode = rte_eal_iova_mode();\n+\tif (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA && iova_mode != RTE_IOVA_VA) {\n+\t\tPLATFORM_LOG(ERR, \"driver %s expects VA IOVA mode but current mode is PA\\n\",\n+\t\t\t     pdrv->driver.name);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = device_setup(pdev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = driver_call_probe(pdrv, pdev);\n+\tif (ret)\n+\t\tdevice_cleanup(pdev);\n+\n+\treturn ret;\n+}\n+\n+static bool\n+driver_match_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)\n+{\n+\tbool match = false;\n+\tchar *kdrv;\n+\n+\tkdrv = dev_kernel_driver_name(pdev->name);\n+\tif (!kdrv)\n+\t\treturn false;\n+\n+\t/* match by driver name */\n+\tif (!strcmp(kdrv, pdrv->driver.name)) {\n+\t\tmatch = true;\n+\t\tgoto out;\n+\t}\n+\n+\t/* match by driver alias */\n+\tif (pdrv->driver.alias != NULL && !strcmp(kdrv, pdrv->driver.alias)) {\n+\t\tmatch = true;\n+\t\tgoto out;\n+\t}\n+\n+\t/* match by device name */\n+\tif (!strcmp(pdev->name, pdrv->driver.name))\n+\t\tmatch = true;\n+\n+out:\n+\tfree(kdrv);\n+\n+\treturn match;\n+}\n+\n+\n+static int\n+device_attach(struct rte_platform_device *pdev)\n+{\n+\tstruct rte_platform_driver *pdrv;\n+\n+\tFOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) {\n+\t\tif (driver_match_device(pdrv, pdev))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (pdrv == NULL)\n+\t\treturn -ENODEV;\n+\n+\treturn driver_probe_device(pdrv, pdev);\n+}\n+\n+static int\n+platform_bus_probe(void)\n+{\n+\tstruct rte_platform_device *pdev;\n+\tint ret;\n+\n+\tFOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {\n+\t\tret = device_attach(pdev);\n+\t\tif (ret == -EBUSY) {\n+\t\t\tPLATFORM_LOG(DEBUG, \"device %s already probed\\n\", pdev->name);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (ret)\n+\t\t\tPLATFORM_LOG(ERR, \"failed to probe %s\\n\", pdev->name);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static struct rte_device *\n+platform_bus_find_device(const struct rte_device *start, rte_dev_cmp_t cmp, const void *data)\n+{\n+\tstruct rte_platform_device *pdev;\n+\n+\tpdev = start ? RTE_TAILQ_NEXT(RTE_DEV_TO_PLATFORM_DEV_CONST(start), next) :\n+\t\t       RTE_TAILQ_FIRST(&platform_bus.device_list);\n+\twhile (pdev) {\n+\t\tif (cmp(&pdev->device, data) == 0)\n+\t\t\treturn &pdev->device;\n+\n+\t\tpdev = RTE_TAILQ_NEXT(pdev, next);\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+platform_bus_plug(struct rte_device *dev)\n+{\n+\tstruct rte_platform_device *pdev;\n+\n+\tif (!dev_allowed(dev->name))\n+\t\treturn -EPERM;\n+\n+\tif (!dev_is_bound_vfio_platform(dev->name))\n+\t\treturn -EPERM;\n+\n+\tpdev = RTE_DEV_TO_PLATFORM_DEV(dev);\n+\tif (pdev == NULL)\n+\t\treturn -EINVAL;\n+\n+\treturn device_attach(pdev);\n+}\n+\n+static void\n+device_release_driver(struct rte_platform_device *pdev)\n+{\n+\tstruct rte_platform_driver *pdrv;\n+\tint ret;\n+\n+\tpdrv = pdev->driver;\n+\tif (pdrv != NULL && pdrv->remove != NULL) {\n+\t\tret = pdrv->remove(pdev);\n+\t\tif (ret)\n+\t\t\tPLATFORM_LOG(WARNING, \"failed to remove %s\\n\", pdev->name);\n+\t}\n+\n+\tpdev->device.driver = NULL;\n+\tpdev->driver = NULL;\n+}\n+\n+static int\n+platform_bus_unplug(struct rte_device *dev)\n+{\n+\tstruct rte_platform_device *pdev;\n+\n+\tpdev = RTE_DEV_TO_PLATFORM_DEV(dev);\n+\tif (pdev == NULL)\n+\t\treturn -EINVAL;\n+\n+\tdevice_release_driver(pdev);\n+\tdevice_cleanup(pdev);\n+\trte_devargs_remove(pdev->device.devargs);\n+\tfree(pdev);\n+\n+\treturn 0;\n+}\n+\n+static int\n+platform_bus_parse(const char *name, void *addr)\n+{\n+\tstruct rte_platform_device *pdev;\n+\tconst char **out = addr;\n+\n+\tFOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {\n+\t\tif (!strcmp(name, pdev->name))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (pdev && addr)\n+\t\t*out = name;\n+\n+\treturn pdev ? 0 : -ENODEV;\n+}\n+\n+static int\n+platform_bus_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)\n+{\n+\tstruct rte_platform_device *pdev;\n+\n+\tpdev = RTE_DEV_TO_PLATFORM_DEV(dev);\n+\tif (pdev == NULL || pdev->driver == NULL) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\n+\tif (pdev->driver->dma_map != NULL)\n+\t\treturn pdev->driver->dma_map(pdev, addr, iova, len);\n+\n+\treturn rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova, len);\n+}\n+\n+static int\n+platform_bus_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)\n+{\n+\tstruct rte_platform_device *pdev;\n+\n+\tpdev = RTE_DEV_TO_PLATFORM_DEV(dev);\n+\tif (pdev == NULL || pdev->driver == NULL) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\n+\tif (pdev->driver->dma_unmap != NULL)\n+\t\treturn pdev->driver->dma_unmap(pdev, addr, iova, len);\n+\n+\treturn rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova,\n+\t\t\t\t\t    len);\n+}\n+\n+static enum rte_iova_mode\n+platform_bus_get_iommu_class(void)\n+{\n+\tstruct rte_platform_driver *pdrv;\n+\tstruct rte_platform_device *pdev;\n+\n+\tFOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {\n+\t\tpdrv = pdev->driver;\n+\t\tif (pdrv != NULL && pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA)\n+\t\t\treturn RTE_IOVA_VA;\n+\t}\n+\n+\treturn RTE_IOVA_DC;\n+}\n+\n+static int\n+platform_bus_cleanup(void)\n+{\n+\tstruct rte_platform_device *pdev, *tmp;\n+\n+\tRTE_TAILQ_FOREACH_SAFE(pdev, &platform_bus.device_list, next, tmp) {\n+\t\tplatform_bus_unplug(&pdev->device);\n+\t\tTAILQ_REMOVE(&platform_bus.device_list, pdev, next);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+struct rte_platform_bus platform_bus = {\n+\t.bus = {\n+\t\t.scan = platform_bus_scan,\n+\t\t.probe = platform_bus_probe,\n+\t\t.find_device = platform_bus_find_device,\n+\t\t.plug = platform_bus_plug,\n+\t\t.unplug = platform_bus_unplug,\n+\t\t.parse = platform_bus_parse,\n+\t\t.dma_map = platform_bus_dma_map,\n+\t\t.dma_unmap = platform_bus_dma_unmap,\n+\t\t.get_iommu_class = platform_bus_get_iommu_class,\n+\t\t.dev_iterate = platform_bus_dev_iterate,\n+\t\t.cleanup = platform_bus_cleanup,\n+\t},\n+\t.device_list = TAILQ_HEAD_INITIALIZER(platform_bus.device_list),\n+\t.driver_list = TAILQ_HEAD_INITIALIZER(platform_bus.driver_list),\n+};\n+\n+RTE_REGISTER_BUS(platform_bus, platform_bus.bus);\n+RTE_LOG_REGISTER_DEFAULT(platform_bus_logtype, NOTICE);\ndiff --git a/drivers/bus/platform/platform_params.c b/drivers/bus/platform/platform_params.c\nnew file mode 100644\nindex 0000000000..d199c0c586\n--- /dev/null\n+++ b/drivers/bus/platform/platform_params.c\n@@ -0,0 +1,70 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell.\n+ */\n+\n+#include <string.h>\n+#include <errno.h>\n+\n+#include <rte_bus.h>\n+#include <rte_common.h>\n+#include <rte_dev.h>\n+#include <rte_errno.h>\n+#include <rte_kvargs.h>\n+\n+#include \"bus_platform_driver.h\"\n+#include \"private.h\"\n+\n+enum platform_params {\n+\tRTE_PLATFORM_PARAM_NAME,\n+};\n+\n+static const char * const platform_params_keys[] = {\n+\t[RTE_PLATFORM_PARAM_NAME] = \"name\",\n+\tNULL\n+};\n+\n+static int\n+platform_dev_match(const struct rte_device *dev, const void *_kvlist)\n+{\n+\tconst char *key = platform_params_keys[RTE_PLATFORM_PARAM_NAME];\n+\tconst struct rte_kvargs *kvlist = _kvlist;\n+\tconst char *name;\n+\n+\t/* no kvlist arg, all devices match */\n+\tif (kvlist == NULL)\n+\t\treturn 0;\n+\n+\t/* if key is present in kvlist and does not match, filter device */\n+\tname = rte_kvargs_get(kvlist, key);\n+\tif (name != NULL && strcmp(name, dev->name))\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+void *\n+platform_bus_dev_iterate(const void *start, const char *str,\n+\t\t\t const struct rte_dev_iterator *it __rte_unused)\n+{\n+\trte_bus_find_device_t find_device;\n+\tstruct rte_kvargs *kvargs = NULL;\n+\tstruct rte_device *dev;\n+\n+\tif (str != NULL) {\n+\t\tkvargs = rte_kvargs_parse(str, platform_params_keys);\n+\t\tif (!kvargs) {\n+\t\t\tPLATFORM_LOG(ERR, \"cannot parse argument list %s\", str);\n+\t\t\trte_errno = EINVAL;\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\tfind_device = platform_bus.bus.find_device;\n+\tif (find_device == NULL)\n+\t\treturn NULL;\n+\n+\tdev = platform_bus.bus.find_device(start, platform_dev_match, kvargs);\n+\trte_kvargs_free(kvargs);\n+\n+\treturn dev;\n+}\ndiff --git a/drivers/bus/platform/private.h b/drivers/bus/platform/private.h\nnew file mode 100644\nindex 0000000000..dcd992f8a7\n--- /dev/null\n+++ b/drivers/bus/platform/private.h\n@@ -0,0 +1,48 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2023 Marvell.\n+ */\n+\n+#ifndef _PLATFORM_PRIVATE_H_\n+#define _PLATFORM_PRIVATE_H_\n+\n+#include <bus_driver.h>\n+#include <rte_bus.h>\n+#include <rte_common.h>\n+#include <rte_dev.h>\n+#include <rte_log.h>\n+#include <rte_os.h>\n+\n+#include \"bus_platform_driver.h\"\n+\n+extern struct rte_platform_bus platform_bus;\n+extern int platform_bus_logtype;\n+\n+/* Platform bus iterators. */\n+#define FOREACH_DEVICE_ON_PLATFORM_BUS(p) \\\n+\tRTE_TAILQ_FOREACH(p, &(platform_bus.device_list), next)\n+\n+#define FOREACH_DRIVER_ON_PLATFORM_BUS(p) \\\n+\tRTE_TAILQ_FOREACH(p, &(platform_bus.driver_list), next)\n+\n+/*\n+ * Structure describing platform bus.\n+ */\n+struct rte_platform_bus {\n+\tstruct rte_bus bus; /* Core bus */\n+\tRTE_TAILQ_HEAD(, rte_platform_device) device_list; /* List of bus devices */\n+\tRTE_TAILQ_HEAD(, rte_platform_driver) driver_list; /* List of bus drivers */\n+};\n+\n+#define PLATFORM_LOG(level, ...) \\\n+\trte_log(RTE_LOG_ ## level, platform_bus_logtype, \\\n+\t\tRTE_FMT(\"platform bus: \" RTE_FMT_HEAD(__VA_ARGS__,), \\\n+\t\t\tRTE_FMT_TAIL(__VA_ARGS__,)))\n+\n+/*\n+ * Iterate registered platform devices and find one that matches provided string.\n+ */\n+void *\n+platform_bus_dev_iterate(const void *start, const char *str,\n+\t\t\t const struct rte_dev_iterator *it __rte_unused);\n+\n+#endif /* _PLATFORM_PRIVATE_H_ */\ndiff --git a/drivers/bus/platform/version.map b/drivers/bus/platform/version.map\nnew file mode 100644\nindex 0000000000..bacce4da08\n--- /dev/null\n+++ b/drivers/bus/platform/version.map\n@@ -0,0 +1,10 @@\n+DPDK_23 {\n+\tlocal: *;\n+};\n+\n+INTERNAL {\n+\tglobal:\n+\n+\trte_platform_register;\n+\trte_platform_unregister;\n+};\n",
    "prefixes": [
        "2/2"
    ]
}