Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2019 Xilinx, Inc. */ #include <linux/dma-mapping.h> #include <linux/fpga/fpga-mgr.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/string.h> #include <linux/firmware/xlnx-zynqmp.h> /* Constant Definitions */ #define IXR_FPGA_DONE_MASK BIT(3) /** * struct zynqmp_fpga_priv - Private data structure * @dev: Device data structure * @flags: flags which is used to identify the bitfile type */ struct zynqmp_fpga_priv { struct device *dev; u32 flags; }; static int zynqmp_fpga_ops_write_init(struct fpga_manager *mgr, struct fpga_image_info *info, const char *buf, size_t size) { struct zynqmp_fpga_priv *priv; priv = mgr->priv; priv->flags = info->flags; return 0; } static int zynqmp_fpga_ops_write(struct fpga_manager *mgr, const char *buf, size_t size) { struct zynqmp_fpga_priv *priv; dma_addr_t dma_addr; u32 eemi_flags = 0; char *kbuf; int ret; priv = mgr->priv; kbuf = dma_alloc_coherent(priv->dev, size, &dma_addr, GFP_KERNEL); if (!kbuf) return -ENOMEM; memcpy(kbuf, buf, size); wmb(); /* ensure all writes are done before initiate FW call */ if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG) eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL; ret = zynqmp_pm_fpga_load(dma_addr, size, eemi_flags); dma_free_coherent(priv->dev, size, kbuf, dma_addr); return ret; } static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr) { u32 status = 0; zynqmp_pm_fpga_get_status(&status); if (status & IXR_FPGA_DONE_MASK) return FPGA_MGR_STATE_OPERATING; return FPGA_MGR_STATE_UNKNOWN; } static const struct fpga_manager_ops zynqmp_fpga_ops = { .state = zynqmp_fpga_ops_state, .write_init = zynqmp_fpga_ops_write_init, .write = zynqmp_fpga_ops_write, }; static int zynqmp_fpga_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct zynqmp_fpga_priv *priv; struct fpga_manager *mgr; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager", &zynqmp_fpga_ops, priv); return PTR_ERR_OR_ZERO(mgr); } #ifdef CONFIG_OF static const struct of_device_id zynqmp_fpga_of_match[] = { { .compatible = "xlnx,zynqmp-pcap-fpga", }, {}, }; MODULE_DEVICE_TABLE(of, zynqmp_fpga_of_match); #endif static struct platform_driver zynqmp_fpga_driver = { .probe = zynqmp_fpga_probe, .driver = { .name = "zynqmp_fpga_manager", .of_match_table = of_match_ptr(zynqmp_fpga_of_match), }, }; module_platform_driver(zynqmp_fpga_driver); MODULE_AUTHOR("Nava kishore Manne <navam@xilinx.com>"); MODULE_DESCRIPTION("Xilinx ZynqMp FPGA Manager"); MODULE_LICENSE("GPL"); |