aboutsummaryrefslogtreecommitdiffstats
path: root/sources/xdma_driver/xdma/xdma_mod.c
diff options
context:
space:
mode:
Diffstat (limited to 'sources/xdma_driver/xdma/xdma_mod.c')
-rw-r--r--sources/xdma_driver/xdma/xdma_mod.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/sources/xdma_driver/xdma/xdma_mod.c b/sources/xdma_driver/xdma/xdma_mod.c
new file mode 100644
index 0000000..d6f724a
--- /dev/null
+++ b/sources/xdma_driver/xdma/xdma_mod.c
@@ -0,0 +1,347 @@
+/*
+ * This file is part of the Xilinx DMA IP Core driver for Linux
+ *
+ * Copyright (c) 2016-present, Xilinx, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/aer.h>
+/* include early, to verify it depends only on the headers above */
+#include "libxdma_api.h"
+#include "libxdma.h"
+#include "xdma_mod.h"
+#include "xdma_cdev.h"
+#include "version.h"
+
+#define DRV_MODULE_NAME "xdma"
+#define DRV_MODULE_DESC "Xilinx XDMA Reference Driver"
+#define DRV_MODULE_RELDATE "Feb. 2018"
+
+static char version[] =
+ DRV_MODULE_DESC " " DRV_MODULE_NAME " v" DRV_MODULE_VERSION "\n";
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION(DRV_MODULE_DESC);
+MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* SECTION: Module global variables */
+static int xpdev_cnt = 0;
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(0x10ee, 0x903f), },
+ { PCI_DEVICE(0x10ee, 0x9038), },
+ { PCI_DEVICE(0x10ee, 0x9028), },
+ { PCI_DEVICE(0x10ee, 0x9018), },
+ { PCI_DEVICE(0x10ee, 0x9034), },
+ { PCI_DEVICE(0x10ee, 0x9024), },
+ { PCI_DEVICE(0x10ee, 0x9014), },
+ { PCI_DEVICE(0x10ee, 0x9032), },
+ { PCI_DEVICE(0x10ee, 0x9022), },
+ { PCI_DEVICE(0x10ee, 0x9012), },
+ { PCI_DEVICE(0x10ee, 0x9031), },
+ { PCI_DEVICE(0x10ee, 0x9021), },
+ { PCI_DEVICE(0x10ee, 0x9011), },
+
+ { PCI_DEVICE(0x10ee, 0x8011), },
+ { PCI_DEVICE(0x10ee, 0x8012), },
+ { PCI_DEVICE(0x10ee, 0x8014), },
+ { PCI_DEVICE(0x10ee, 0x8018), },
+ { PCI_DEVICE(0x10ee, 0x8021), },
+ { PCI_DEVICE(0x10ee, 0x8022), },
+ { PCI_DEVICE(0x10ee, 0x8024), },
+ { PCI_DEVICE(0x10ee, 0x8028), },
+ { PCI_DEVICE(0x10ee, 0x8031), },
+ { PCI_DEVICE(0x10ee, 0x8032), },
+ { PCI_DEVICE(0x10ee, 0x8034), },
+ { PCI_DEVICE(0x10ee, 0x8038), },
+
+ { PCI_DEVICE(0x10ee, 0x7011), },
+ { PCI_DEVICE(0x10ee, 0x7012), },
+ { PCI_DEVICE(0x10ee, 0x7014), },
+ { PCI_DEVICE(0x10ee, 0x7018), },
+ { PCI_DEVICE(0x10ee, 0x7021), },
+ { PCI_DEVICE(0x10ee, 0x7022), },
+ { PCI_DEVICE(0x10ee, 0x7024), },
+ { PCI_DEVICE(0x10ee, 0x7028), },
+ { PCI_DEVICE(0x10ee, 0x7031), },
+ { PCI_DEVICE(0x10ee, 0x7032), },
+ { PCI_DEVICE(0x10ee, 0x7034), },
+ { PCI_DEVICE(0x10ee, 0x7038), },
+
+ { PCI_DEVICE(0x10ee, 0x6828), },
+ { PCI_DEVICE(0x10ee, 0x6830), },
+ { PCI_DEVICE(0x10ee, 0x6928), },
+ { PCI_DEVICE(0x10ee, 0x6930), },
+ { PCI_DEVICE(0x10ee, 0x6A28), },
+ { PCI_DEVICE(0x10ee, 0x6A30), },
+ { PCI_DEVICE(0x10ee, 0x6D30), },
+
+ { PCI_DEVICE(0x10ee, 0x4808), },
+ { PCI_DEVICE(0x10ee, 0x4828), },
+ { PCI_DEVICE(0x10ee, 0x4908), },
+ { PCI_DEVICE(0x10ee, 0x4A28), },
+ { PCI_DEVICE(0x10ee, 0x4B28), },
+
+ { PCI_DEVICE(0x10ee, 0x2808), },
+
+#ifdef INTERNAL_TESTING
+ { PCI_DEVICE(0x1d0f, 0x1042), 0},
+#endif
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static void xpdev_free(struct xdma_pci_dev *xpdev)
+{
+ struct xdma_dev *xdev = xpdev->xdev;
+
+ pr_info("xpdev 0x%p, destroy_interfaces, xdev 0x%p.\n", xpdev, xdev);
+ xpdev_destroy_interfaces(xpdev);
+ xpdev->xdev = NULL;
+ pr_info("xpdev 0x%p, xdev 0x%p xdma_device_close.\n", xpdev, xdev);
+ xdma_device_close(xpdev->pdev, xdev);
+ xpdev_cnt--;
+
+ kfree(xpdev);
+}
+
+static struct xdma_pci_dev *xpdev_alloc(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev = kmalloc(sizeof(*xpdev), GFP_KERNEL);
+
+ if (!xpdev)
+ return NULL;
+ memset(xpdev, 0, sizeof(*xpdev));
+
+ xpdev->magic = MAGIC_DEVICE;
+ xpdev->pdev = pdev;
+ xpdev->user_max = MAX_USER_IRQ;
+ xpdev->h2c_channel_max = XDMA_CHANNEL_NUM_MAX;
+ xpdev->c2h_channel_max = XDMA_CHANNEL_NUM_MAX;
+
+ xpdev_cnt++;
+ return xpdev;
+}
+
+static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int rv = 0;
+ struct xdma_pci_dev *xpdev = NULL;
+ struct xdma_dev *xdev;
+ void *hndl;
+
+ xpdev = xpdev_alloc(pdev);
+ if (!xpdev)
+ return -ENOMEM;
+
+ hndl = xdma_device_open(DRV_MODULE_NAME, pdev, &xpdev->user_max,
+ &xpdev->h2c_channel_max, &xpdev->c2h_channel_max);
+ if (!hndl)
+ return -EINVAL;
+
+ BUG_ON(xpdev->user_max > MAX_USER_IRQ);
+ BUG_ON(xpdev->h2c_channel_max > XDMA_CHANNEL_NUM_MAX);
+ BUG_ON(xpdev->c2h_channel_max > XDMA_CHANNEL_NUM_MAX);
+
+ if (!xpdev->h2c_channel_max && !xpdev->c2h_channel_max)
+ pr_warn("NO engine found!\n");
+
+ if (xpdev->user_max) {
+ u32 mask = (1 << (xpdev->user_max + 1)) - 1;
+
+ rv = xdma_user_isr_enable(hndl, mask);
+ if (rv)
+ goto err_out;
+ }
+
+ /* make sure no duplicate */
+ xdev = xdev_find_by_pdev(pdev);
+ if (!xdev) {
+ pr_warn("NO xdev found!\n");
+ return -EINVAL;
+ }
+ BUG_ON(hndl != xdev );
+
+ pr_info("%s xdma%d, pdev 0x%p, xdev 0x%p, 0x%p, usr %d, ch %d,%d.\n",
+ dev_name(&pdev->dev), xdev->idx, pdev, xpdev, xdev,
+ xpdev->user_max, xpdev->h2c_channel_max,
+ xpdev->c2h_channel_max);
+
+ xpdev->xdev = hndl;
+
+ rv = xpdev_create_interfaces(xpdev);
+ if (rv)
+ goto err_out;
+
+ dev_set_drvdata(&pdev->dev, xpdev);
+
+ return 0;
+
+err_out:
+ pr_err("pdev 0x%p, err %d.\n", pdev, rv);
+ xpdev_free(xpdev);
+ return rv;
+}
+
+static void remove_one(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev;
+
+ if (!pdev)
+ return;
+
+ xpdev = dev_get_drvdata(&pdev->dev);
+ if (!xpdev)
+ return;
+
+ pr_info("pdev 0x%p, xdev 0x%p, 0x%p.\n",
+ pdev, xpdev, xpdev->xdev);
+ xpdev_free(xpdev);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static pci_ers_result_t xdma_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ pr_warn("dev 0x%p,0x%p, frozen state error, reset controller\n",
+ pdev, xpdev);
+ xdma_device_offline(pdev, xpdev->xdev);
+ pci_disable_device(pdev);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ pr_warn("dev 0x%p,0x%p, failure state error, req. disconnect\n",
+ pdev, xpdev);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t xdma_slot_reset(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ pr_info("0x%p restart after slot reset\n", xpdev);
+ if (pci_enable_device_mem(pdev)) {
+ pr_info("0x%p failed to renable after slot reset\n", xpdev);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
+ pci_save_state(pdev);
+ xdma_device_online(pdev, xpdev->xdev);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void xdma_error_resume(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ pr_info("dev 0x%p,0x%p.\n", pdev, xpdev);
+#if KERNEL_VERSION(5, 7, 0) <= LINUX_VERSION_CODE
+ pci_aer_clear_nonfatal_status(pdev);
+#else
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
+static void xdma_reset_prepare(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ pr_info("dev 0x%p,0x%p.\n", pdev, xpdev);
+ xdma_device_offline(pdev, xpdev->xdev);
+}
+
+static void xdma_reset_done(struct pci_dev *pdev)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ pr_info("dev 0x%p,0x%p.\n", pdev, xpdev);
+ xdma_device_online(pdev, xpdev->xdev);
+}
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+static void xdma_reset_notify(struct pci_dev *pdev, bool prepare)
+{
+ struct xdma_pci_dev *xpdev = dev_get_drvdata(&pdev->dev);
+
+ pr_info("dev 0x%p,0x%p, prepare %d.\n", pdev, xpdev, prepare);
+
+ if (prepare)
+ xdma_device_offline(pdev, xpdev->xdev);
+ else
+ xdma_device_online(pdev, xpdev->xdev);
+}
+#endif
+
+static const struct pci_error_handlers xdma_err_handler = {
+ .error_detected = xdma_error_detected,
+ .slot_reset = xdma_slot_reset,
+ .resume = xdma_error_resume,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
+ .reset_prepare = xdma_reset_prepare,
+ .reset_done = xdma_reset_done,
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+ .reset_notify = xdma_reset_notify,
+#endif
+};
+
+static struct pci_driver pci_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = pci_ids,
+ .probe = probe_one,
+ .remove = remove_one,
+ .err_handler = &xdma_err_handler,
+};
+
+static int __init xdma_mod_init(void)
+{
+ int rv;
+ extern unsigned int desc_blen_max;
+ extern unsigned int sgdma_timeout;
+
+ pr_info("%s", version);
+
+ if (desc_blen_max > XDMA_DESC_BLEN_MAX)
+ desc_blen_max = XDMA_DESC_BLEN_MAX;
+ pr_info("desc_blen_max: 0x%x/%u, sgdma_timeout: %u sec.\n",
+ desc_blen_max, desc_blen_max, sgdma_timeout);
+
+ rv = xdma_cdev_init();
+ if (rv < 0)
+ return rv;
+
+ return pci_register_driver(&pci_driver);
+}
+
+static void __exit xdma_mod_exit(void)
+{
+ /* unregister this driver from the PCI bus driver */
+ dbg_init("pci_unregister_driver.\n");
+ pci_unregister_driver(&pci_driver);
+ xdma_cdev_cleanup();
+}
+
+module_init(xdma_mod_init);
+module_exit(xdma_mod_exit);