@@ -12,3 +12,5 @@ config CDX_BUS
Driver to enable CDX Bus infrastructure. CDX bus uses
CDX controller and firmware to scan the FPGA based
devices.
+
+source "drivers/bus/cdx/controller/Kconfig"
@@ -5,4 +5,4 @@
# Copyright (C) 2022, Advanced Micro Devices, Inc.
#
-obj-$(CONFIG_CDX_BUS) += cdx.o cdx_msi.o
+obj-$(CONFIG_CDX_BUS) += cdx.o cdx_msi.o controller/
new file mode 100644
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# CDX controller configuration
+#
+# Copyright (C) 2022, Advanced Micro Devices, Inc.
+#
+
+config CDX_CONTROLLER
+ tristate "CDX hardware driver"
+ depends on CDX_BUS
+ help
+ CDX controller drives the CDX bus. It interacts with
+ firmware to get the hardware devices and registers with
+ the CDX bus. Say Y to enable the CDX hardware driver.
+
+ If unsure, say N.
new file mode 100644
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for CDX controller drivers
+#
+# Copyright (C) 2022, Advanced Micro Devices, Inc.
+#
+
+obj-$(CONFIG_CDX_CONTROLLER) += cdx_controller.o mcdi_stubs.o
new file mode 100644
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Platform driver for CDX bus.
+ *
+ * Copyright (C) 2022, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+#include <linux/cdx/cdx_bus.h>
+
+#include "../cdx.h"
+#include "mcdi_stubs.h"
+
+static int cdx_reset_device(struct cdx_controller_t *cdx,
+ uint8_t bus_num, uint8_t dev_num)
+{
+ return cdx_mcdi_reset_dev(cdx->priv, bus_num, dev_num);
+}
+
+static int cdx_scan_devices(struct cdx_controller_t *cdx)
+{
+ struct cdx_mcdi_t *cdx_mcdi = cdx->priv;
+ int num_cdx_bus, num_cdx_dev;
+ uint8_t bus_num, dev_num;
+ int ret;
+
+ /* MCDI FW Read: Fetch the number of CDX buses present*/
+ num_cdx_bus = cdx_mcdi_get_num_buses(cdx_mcdi);
+
+ for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) {
+ /* MCDI FW Read: Fetch the number of devices present */
+ num_cdx_dev = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num);
+
+ for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) {
+ struct cdx_dev_params_t dev_params;
+
+ /* MCDI FW: Get the device config */
+ ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num,
+ dev_num, &dev_params);
+ if (ret) {
+ dev_err(cdx->dev,
+ "CDX device config get failed for bus: %d\n", ret);
+ return ret;
+ }
+ dev_params.cdx = cdx;
+
+ /* Add the device to the cdx bus */
+ ret = cdx_device_add(&dev_params);
+ if (ret) {
+ dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n",
+ dev_num, ret);
+ return ret;
+ }
+
+ dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n",
+ dev_num, bus_num);
+ }
+ }
+
+ return 0;
+}
+
+static int cdx_write_msi(struct cdx_controller_t *cdx, uint8_t bus_num,
+ uint8_t dev_num, uint16_t msi_index,
+ uint32_t data, uint64_t addr)
+{
+ return cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num,
+ msi_index, data, addr);
+}
+
+static int cdx_probe(struct platform_device *pdev)
+{
+ struct cdx_mcdi_t *cdx_mcdi;
+ struct cdx_controller_t *cdx;
+ struct device_node *iommu_node;
+ struct platform_device *plat_iommu_dev;
+ int ret;
+
+ /*
+ * Defer probe CDX controller in case IOMMU is not yet initialized.
+ * We do not add CDX devices before the IOMMU device is probed, as
+ * in cleanup or shutdown time (device_shutdonw()), which traverse
+ * device list in reverse order of device addition, we need to remove
+ * CDX devices, before IOMMU device is removed.
+ */
+ iommu_node = of_parse_phandle(pdev->dev.of_node, "iommu-map", 1);
+ if (!of_device_is_available(iommu_node)) {
+ dev_err(&pdev->dev, "No associated IOMMU node found\n");
+ return -EINVAL;
+ }
+
+ plat_iommu_dev = of_find_device_by_node(iommu_node);
+ if (!plat_iommu_dev) {
+ ret = -EPROBE_DEFER;
+ goto iommu_of_cleanup;
+ }
+
+ if (!plat_iommu_dev->dev.driver) {
+ ret = -EPROBE_DEFER;
+ goto iommu_of_cleanup;
+ }
+
+ cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL);
+ if (!cdx_mcdi) {
+ dev_err(&pdev->dev, "Failed to allocate memory for cdx_mcdi\n");
+ ret = -ENOMEM;
+ goto iommu_of_cleanup;
+ }
+
+ /* MCDI FW: Initialize the FW path */
+ ret = cdx_mcdi_init(cdx_mcdi);
+ if (ret) {
+ dev_err(&pdev->dev, "MCDI Initialization failed: %d\n", ret);
+ goto mcdi_init_fail;
+ }
+
+ cdx = kzalloc(sizeof(*cdx), GFP_KERNEL);
+ if (!cdx) {
+ dev_err(&pdev->dev, "Failed to allocate memory for cdx\n");
+ ret = -ENOMEM;
+ goto cdx_alloc_fail;
+ }
+ platform_set_drvdata(pdev, cdx);
+
+ cdx->dev = &pdev->dev;
+ cdx->priv = cdx_mcdi;
+ cdx->ops.scan = cdx_scan_devices;
+ cdx->ops.reset_dev = cdx_reset_device;
+ cdx->ops.write_msi = cdx_write_msi;
+
+ /* Register CDX controller with CDX bus driver */
+ ret = cdx_register_controller(cdx);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register CDX controller\n");
+ goto cdx_register_fail;
+ }
+
+ return 0;
+
+cdx_register_fail:
+ kfree(cdx);
+cdx_alloc_fail:
+ cdx_mcdi_finish(cdx_mcdi);
+mcdi_init_fail:
+ kfree(cdx_mcdi);
+iommu_of_cleanup:
+ of_node_put(iommu_node);
+
+ return ret;
+}
+
+static int cdx_remove(struct platform_device *pdev)
+{
+ struct cdx_controller_t *cdx = platform_get_drvdata(pdev);
+ struct cdx_mcdi_t *cdx_mcdi = cdx->priv;
+
+ cdx_unregister_controller(cdx);
+ kfree(cdx);
+
+ cdx_mcdi_finish(cdx_mcdi);
+ kfree(cdx_mcdi);
+
+ return 0;
+}
+
+static void cdx_shutdown(struct platform_device *pdev)
+{
+ cdx_remove(pdev);
+}
+
+static const struct of_device_id cdx_match_table[] = {
+ {.compatible = "xlnx,cdxbus-controller-1.0",},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, cdx_match_table);
+
+static struct platform_driver cdx_pdriver = {
+ .driver = {
+ .name = "cdx-controller",
+ .pm = NULL,
+ .of_match_table = cdx_match_table,
+ },
+ .probe = cdx_probe,
+ .remove = cdx_remove,
+ .shutdown = cdx_shutdown,
+};
+
+static int __init cdx_controller_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&cdx_pdriver);
+ if (ret < 0)
+ pr_err("platform_driver_register() failed: %d\n", ret);
+
+ return ret;
+}
+module_init(cdx_controller_init);
+
+static void __exit cdx_controller_exit(void)
+{
+ platform_driver_unregister(&cdx_pdriver);
+}
+module_exit(cdx_controller_exit);
new file mode 100644
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MCDI Firmware interaction for CDX bus.
+ *
+ * Copyright (C) 2022, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/ioport.h>
+
+#include "mcdi_stubs.h"
+
+int cdx_mcdi_init(struct cdx_mcdi_t *cdx_mcdi)
+{
+ cdx_mcdi->id = 0;
+ cdx_mcdi->flags = 0;
+
+ return 0;
+}
+
+void cdx_mcdi_finish(struct cdx_mcdi_t *cdx_mcdi)
+{
+}
+
+int cdx_mcdi_get_num_buses(struct cdx_mcdi_t *cdx_mcdi)
+{
+ return 1;
+}
+
+int cdx_mcdi_get_num_devs(struct cdx_mcdi_t *cdx_mcdi, int bus_num)
+{
+ return 1;
+}
+
+int cdx_mcdi_get_dev_config(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num,
+ struct cdx_dev_params_t *dev_params)
+{
+ dev_params->res[0].start = 0xe4020000;
+ dev_params->res[0].end = 0xe4020FFF;
+ dev_params->res[0].flags = IORESOURCE_MEM;
+ dev_params->res[1].start = 0xe4100000;
+ dev_params->res[1].end = 0xE411FFFF;
+ dev_params->res[1].flags = IORESOURCE_MEM;
+ dev_params->res_count = 2;
+
+ dev_params->req_id = 0x250;
+ dev_params->num_msi = 4;
+ dev_params->vendor = 0x10ee;
+ dev_params->device = 0x8084;
+ dev_params->bus_num = bus_num;
+ dev_params->dev_num = dev_num;
+
+ return 0;
+}
+
+int cdx_mcdi_reset_dev(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num)
+{
+ return 0;
+}
+
+int cdx_mcdi_write_msi(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num,
+ uint16_t msi_index, uint32_t data,
+ uint64_t addr)
+{
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Header file for MCDI FW interaction for CDX bus.
+ *
+ * Copyright (C) 2022, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _MCDI_STUBS_H_
+#define _MCDI_STUBS_H_
+
+#include "../cdx.h"
+
+/**
+ * struct cdx_mcdi_t - CDX MCDI Firmware interface, to interact
+ * with CDX controller.
+ * @id: ID for MCDI Firmware interface
+ * @flags: Associated flags
+ */
+struct cdx_mcdi_t {
+ u32 id;
+ u32 flags;
+ /* Have more MCDI interface related data */
+};
+
+/**
+ * cdx_mcdi_init - Initialize the MCDI Firmware interface
+ * for the CDX controller.
+ * @cdx_mcdi: pointer to MCDI interface
+ *
+ * Return 0 on success, <0 on failure
+ */
+int cdx_mcdi_init(struct cdx_mcdi_t *cdx_mcdi);
+
+/**
+ * cdx_mcdi_finish - Close the MCDI Firmware interface.
+ * @cdx_mcdi: pointer to MCDI interface
+ */
+void cdx_mcdi_finish(struct cdx_mcdi_t *cdx_mcdi);
+
+/**
+ * cdx_mcdi_get_num_buses - Get the total number of busses on
+ * the controller.
+ * @cdx_mcdi: pointer to MCDI interface.
+ *
+ * Return total number of busses available on the controller,
+ * <0 on failure
+ */
+int cdx_mcdi_get_num_buses(struct cdx_mcdi_t *cdx_mcdi);
+
+/**
+ * cdx_mcdi_get_num_devs - Get the total number of devices on
+ * a particular bus.
+ * @cdx_mcdi: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ *
+ * Return total number of devices available on the bus, <0 on failure
+ */
+int cdx_mcdi_get_num_devs(struct cdx_mcdi_t *cdx_mcdi, int bus_num);
+
+/**
+ * cdx_mcdi_get_dev_config - Get configuration for a particular
+ * bus_num:dev_num
+ * @cdx_mcdi: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @dev_params: Pointer to cdx_dev_params_t, this is populated by this
+ * function with the configuration corresponding to the provided
+ * bus_num:dev_num.
+ *
+ * Return 0 total number of devices available on the bus, <0 on failure
+ */
+int cdx_mcdi_get_dev_config(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num,
+ struct cdx_dev_params_t *dev_params);
+
+/**
+ * cdx_mcdi_reset_dev - Reset cdx device represented by bus_num:dev_num
+ * @cdx_mcdi: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ *
+ * Return 0 on success, <0 on failure
+ */
+int cdx_mcdi_reset_dev(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num);
+
+/**
+ * cdx_mcdi_write_msi_data - Write MSI related info to the Firmware
+ * @cdx_mcdi: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @msi_index: MSI Index for which the data and address is provided
+ * @data: MSI data (i.e. eventID) for the interrupt
+ * @addr: the address on which the MSI is to be raised
+ */
+int cdx_mcdi_write_msi(struct cdx_mcdi_t *cdx_mcdi,
+ uint8_t bus_num, uint8_t dev_num,
+ uint16_t msi_index, uint32_t data,
+ uint64_t addr);
+
+#endif /* _MCDI_STUBS_H_ */