18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2015 Xilinx Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2015, National Instruments Corp. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * FPGA Manager Driver for Xilinx Zynq, heavily based on xdevcfg driver 78c2ecf20Sopenharmony_ci * in their vendor tree. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/completion.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-mgr.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/pm.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci#include <linux/string.h> 258c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Offsets into SLCR regmap */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* FPGA Software Reset Control */ 308c2ecf20Sopenharmony_ci#define SLCR_FPGA_RST_CTRL_OFFSET 0x240 318c2ecf20Sopenharmony_ci/* Level Shifters Enable */ 328c2ecf20Sopenharmony_ci#define SLCR_LVL_SHFTR_EN_OFFSET 0x900 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Constant Definitions */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Control Register */ 378c2ecf20Sopenharmony_ci#define CTRL_OFFSET 0x00 388c2ecf20Sopenharmony_ci/* Lock Register */ 398c2ecf20Sopenharmony_ci#define LOCK_OFFSET 0x04 408c2ecf20Sopenharmony_ci/* Interrupt Status Register */ 418c2ecf20Sopenharmony_ci#define INT_STS_OFFSET 0x0c 428c2ecf20Sopenharmony_ci/* Interrupt Mask Register */ 438c2ecf20Sopenharmony_ci#define INT_MASK_OFFSET 0x10 448c2ecf20Sopenharmony_ci/* Status Register */ 458c2ecf20Sopenharmony_ci#define STATUS_OFFSET 0x14 468c2ecf20Sopenharmony_ci/* DMA Source Address Register */ 478c2ecf20Sopenharmony_ci#define DMA_SRC_ADDR_OFFSET 0x18 488c2ecf20Sopenharmony_ci/* DMA Destination Address Reg */ 498c2ecf20Sopenharmony_ci#define DMA_DST_ADDR_OFFSET 0x1c 508c2ecf20Sopenharmony_ci/* DMA Source Transfer Length */ 518c2ecf20Sopenharmony_ci#define DMA_SRC_LEN_OFFSET 0x20 528c2ecf20Sopenharmony_ci/* DMA Destination Transfer */ 538c2ecf20Sopenharmony_ci#define DMA_DEST_LEN_OFFSET 0x24 548c2ecf20Sopenharmony_ci/* Unlock Register */ 558c2ecf20Sopenharmony_ci#define UNLOCK_OFFSET 0x34 568c2ecf20Sopenharmony_ci/* Misc. Control Register */ 578c2ecf20Sopenharmony_ci#define MCTRL_OFFSET 0x80 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Control Register Bit definitions */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Signal to reset FPGA */ 628c2ecf20Sopenharmony_ci#define CTRL_PCFG_PROG_B_MASK BIT(30) 638c2ecf20Sopenharmony_ci/* Enable PCAP for PR */ 648c2ecf20Sopenharmony_ci#define CTRL_PCAP_PR_MASK BIT(27) 658c2ecf20Sopenharmony_ci/* Enable PCAP */ 668c2ecf20Sopenharmony_ci#define CTRL_PCAP_MODE_MASK BIT(26) 678c2ecf20Sopenharmony_ci/* Lower rate to allow decrypt on the fly */ 688c2ecf20Sopenharmony_ci#define CTRL_PCAP_RATE_EN_MASK BIT(25) 698c2ecf20Sopenharmony_ci/* System booted in secure mode */ 708c2ecf20Sopenharmony_ci#define CTRL_SEC_EN_MASK BIT(7) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Miscellaneous Control Register bit definitions */ 738c2ecf20Sopenharmony_ci/* Internal PCAP loopback */ 748c2ecf20Sopenharmony_ci#define MCTRL_PCAP_LPBK_MASK BIT(4) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Status register bit definitions */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* FPGA init status */ 798c2ecf20Sopenharmony_ci#define STATUS_DMA_Q_F BIT(31) 808c2ecf20Sopenharmony_ci#define STATUS_DMA_Q_E BIT(30) 818c2ecf20Sopenharmony_ci#define STATUS_PCFG_INIT_MASK BIT(4) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* Interrupt Status/Mask Register Bit definitions */ 848c2ecf20Sopenharmony_ci/* DMA command done */ 858c2ecf20Sopenharmony_ci#define IXR_DMA_DONE_MASK BIT(13) 868c2ecf20Sopenharmony_ci/* DMA and PCAP cmd done */ 878c2ecf20Sopenharmony_ci#define IXR_D_P_DONE_MASK BIT(12) 888c2ecf20Sopenharmony_ci /* FPGA programmed */ 898c2ecf20Sopenharmony_ci#define IXR_PCFG_DONE_MASK BIT(2) 908c2ecf20Sopenharmony_ci#define IXR_ERROR_FLAGS_MASK 0x00F0C860 918c2ecf20Sopenharmony_ci#define IXR_ALL_MASK 0xF8F7F87F 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Miscellaneous constant values */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Invalid DMA addr */ 968c2ecf20Sopenharmony_ci#define DMA_INVALID_ADDRESS GENMASK(31, 0) 978c2ecf20Sopenharmony_ci/* Used to unlock the dev */ 988c2ecf20Sopenharmony_ci#define UNLOCK_MASK 0x757bdf0d 998c2ecf20Sopenharmony_ci/* Timeout for polling reset bits */ 1008c2ecf20Sopenharmony_ci#define INIT_POLL_TIMEOUT 2500000 1018c2ecf20Sopenharmony_ci/* Delay for polling reset bits */ 1028c2ecf20Sopenharmony_ci#define INIT_POLL_DELAY 20 1038c2ecf20Sopenharmony_ci/* Signal this is the last DMA transfer, wait for the AXI and PCAP before 1048c2ecf20Sopenharmony_ci * interrupting 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci#define DMA_SRC_LAST_TRANSFER 1 1078c2ecf20Sopenharmony_ci/* Timeout for DMA completion */ 1088c2ecf20Sopenharmony_ci#define DMA_TIMEOUT_MS 5000 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* Masks for controlling stuff in SLCR */ 1118c2ecf20Sopenharmony_ci/* Disable all Level shifters */ 1128c2ecf20Sopenharmony_ci#define LVL_SHFTR_DISABLE_ALL_MASK 0x0 1138c2ecf20Sopenharmony_ci/* Enable Level shifters from PS to PL */ 1148c2ecf20Sopenharmony_ci#define LVL_SHFTR_ENABLE_PS_TO_PL 0xa 1158c2ecf20Sopenharmony_ci/* Enable Level shifters from PL to PS */ 1168c2ecf20Sopenharmony_ci#define LVL_SHFTR_ENABLE_PL_TO_PS 0xf 1178c2ecf20Sopenharmony_ci/* Enable global resets */ 1188c2ecf20Sopenharmony_ci#define FPGA_RST_ALL_MASK 0xf 1198c2ecf20Sopenharmony_ci/* Disable global resets */ 1208c2ecf20Sopenharmony_ci#define FPGA_RST_NONE_MASK 0x0 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct zynq_fpga_priv { 1238c2ecf20Sopenharmony_ci int irq; 1248c2ecf20Sopenharmony_ci struct clk *clk; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci void __iomem *io_base; 1278c2ecf20Sopenharmony_ci struct regmap *slcr; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci spinlock_t dma_lock; 1308c2ecf20Sopenharmony_ci unsigned int dma_elm; 1318c2ecf20Sopenharmony_ci unsigned int dma_nelms; 1328c2ecf20Sopenharmony_ci struct scatterlist *cur_sg; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci struct completion dma_done; 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline void zynq_fpga_write(struct zynq_fpga_priv *priv, u32 offset, 1388c2ecf20Sopenharmony_ci u32 val) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci writel(val, priv->io_base + offset); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic inline u32 zynq_fpga_read(const struct zynq_fpga_priv *priv, 1448c2ecf20Sopenharmony_ci u32 offset) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return readl(priv->io_base + offset); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define zynq_fpga_poll_timeout(priv, addr, val, cond, sleep_us, timeout_us) \ 1508c2ecf20Sopenharmony_ci readl_poll_timeout(priv->io_base + addr, val, cond, sleep_us, \ 1518c2ecf20Sopenharmony_ci timeout_us) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Cause the specified irq mask bits to generate IRQs */ 1548c2ecf20Sopenharmony_cistatic inline void zynq_fpga_set_irq(struct zynq_fpga_priv *priv, u32 enable) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci zynq_fpga_write(priv, INT_MASK_OFFSET, ~enable); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* Must be called with dma_lock held */ 1608c2ecf20Sopenharmony_cistatic void zynq_step_dma(struct zynq_fpga_priv *priv) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci u32 addr; 1638c2ecf20Sopenharmony_ci u32 len; 1648c2ecf20Sopenharmony_ci bool first; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci first = priv->dma_elm == 0; 1678c2ecf20Sopenharmony_ci while (priv->cur_sg) { 1688c2ecf20Sopenharmony_ci /* Feed the DMA queue until it is full. */ 1698c2ecf20Sopenharmony_ci if (zynq_fpga_read(priv, STATUS_OFFSET) & STATUS_DMA_Q_F) 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci addr = sg_dma_address(priv->cur_sg); 1738c2ecf20Sopenharmony_ci len = sg_dma_len(priv->cur_sg); 1748c2ecf20Sopenharmony_ci if (priv->dma_elm + 1 == priv->dma_nelms) { 1758c2ecf20Sopenharmony_ci /* The last transfer waits for the PCAP to finish too, 1768c2ecf20Sopenharmony_ci * notice this also changes the irq_mask to ignore 1778c2ecf20Sopenharmony_ci * IXR_DMA_DONE_MASK which ensures we do not trigger 1788c2ecf20Sopenharmony_ci * the completion too early. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci addr |= DMA_SRC_LAST_TRANSFER; 1818c2ecf20Sopenharmony_ci priv->cur_sg = NULL; 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci priv->cur_sg = sg_next(priv->cur_sg); 1848c2ecf20Sopenharmony_ci priv->dma_elm++; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, addr); 1888c2ecf20Sopenharmony_ci zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, DMA_INVALID_ADDRESS); 1898c2ecf20Sopenharmony_ci zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, len / 4); 1908c2ecf20Sopenharmony_ci zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, 0); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Once the first transfer is queued we can turn on the ISR, future 1948c2ecf20Sopenharmony_ci * calls to zynq_step_dma will happen from the ISR context. The 1958c2ecf20Sopenharmony_ci * dma_lock spinlock guarentees this handover is done coherently, the 1968c2ecf20Sopenharmony_ci * ISR enable is put at the end to avoid another CPU spinning in the 1978c2ecf20Sopenharmony_ci * ISR on this lock. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci if (first && priv->cur_sg) { 2008c2ecf20Sopenharmony_ci zynq_fpga_set_irq(priv, 2018c2ecf20Sopenharmony_ci IXR_DMA_DONE_MASK | IXR_ERROR_FLAGS_MASK); 2028c2ecf20Sopenharmony_ci } else if (!priv->cur_sg) { 2038c2ecf20Sopenharmony_ci /* The last transfer changes to DMA & PCAP mode since we do 2048c2ecf20Sopenharmony_ci * not want to continue until everything has been flushed into 2058c2ecf20Sopenharmony_ci * the PCAP. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci zynq_fpga_set_irq(priv, 2088c2ecf20Sopenharmony_ci IXR_D_P_DONE_MASK | IXR_ERROR_FLAGS_MASK); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic irqreturn_t zynq_fpga_isr(int irq, void *data) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv = data; 2158c2ecf20Sopenharmony_ci u32 intr_status; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* If anything other than DMA completion is reported stop and hand 2188c2ecf20Sopenharmony_ci * control back to zynq_fpga_ops_write, something went wrong, 2198c2ecf20Sopenharmony_ci * otherwise progress the DMA. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci spin_lock(&priv->dma_lock); 2228c2ecf20Sopenharmony_ci intr_status = zynq_fpga_read(priv, INT_STS_OFFSET); 2238c2ecf20Sopenharmony_ci if (!(intr_status & IXR_ERROR_FLAGS_MASK) && 2248c2ecf20Sopenharmony_ci (intr_status & IXR_DMA_DONE_MASK) && priv->cur_sg) { 2258c2ecf20Sopenharmony_ci zynq_fpga_write(priv, INT_STS_OFFSET, IXR_DMA_DONE_MASK); 2268c2ecf20Sopenharmony_ci zynq_step_dma(priv); 2278c2ecf20Sopenharmony_ci spin_unlock(&priv->dma_lock); 2288c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci spin_unlock(&priv->dma_lock); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci zynq_fpga_set_irq(priv, 0); 2338c2ecf20Sopenharmony_ci complete(&priv->dma_done); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* Sanity check the proposed bitstream. It must start with the sync word in 2398c2ecf20Sopenharmony_ci * the correct byte order, and be dword aligned. The input is a Xilinx .bin 2408c2ecf20Sopenharmony_ci * file with every 32 bit quantity swapped. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic bool zynq_fpga_has_sync(const u8 *buf, size_t count) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci for (; count >= 4; buf += 4, count -= 4) 2458c2ecf20Sopenharmony_ci if (buf[0] == 0x66 && buf[1] == 0x55 && buf[2] == 0x99 && 2468c2ecf20Sopenharmony_ci buf[3] == 0xaa) 2478c2ecf20Sopenharmony_ci return true; 2488c2ecf20Sopenharmony_ci return false; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int zynq_fpga_ops_write_init(struct fpga_manager *mgr, 2528c2ecf20Sopenharmony_ci struct fpga_image_info *info, 2538c2ecf20Sopenharmony_ci const char *buf, size_t count) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv; 2568c2ecf20Sopenharmony_ci u32 ctrl, status; 2578c2ecf20Sopenharmony_ci int err; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci priv = mgr->priv; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = clk_enable(priv->clk); 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* check if bitstream is encrypted & and system's still secure */ 2668c2ecf20Sopenharmony_ci if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) { 2678c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, CTRL_OFFSET); 2688c2ecf20Sopenharmony_ci if (!(ctrl & CTRL_SEC_EN_MASK)) { 2698c2ecf20Sopenharmony_ci dev_err(&mgr->dev, 2708c2ecf20Sopenharmony_ci "System not secure, can't use crypted bitstreams\n"); 2718c2ecf20Sopenharmony_ci err = -EINVAL; 2728c2ecf20Sopenharmony_ci goto out_err; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* don't globally reset PL if we're doing partial reconfig */ 2778c2ecf20Sopenharmony_ci if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { 2788c2ecf20Sopenharmony_ci if (!zynq_fpga_has_sync(buf, count)) { 2798c2ecf20Sopenharmony_ci dev_err(&mgr->dev, 2808c2ecf20Sopenharmony_ci "Invalid bitstream, could not find a sync word. Bitstream must be a byte swapped .bin file\n"); 2818c2ecf20Sopenharmony_ci err = -EINVAL; 2828c2ecf20Sopenharmony_ci goto out_err; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* assert AXI interface resets */ 2868c2ecf20Sopenharmony_ci regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET, 2878c2ecf20Sopenharmony_ci FPGA_RST_ALL_MASK); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* disable all level shifters */ 2908c2ecf20Sopenharmony_ci regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET, 2918c2ecf20Sopenharmony_ci LVL_SHFTR_DISABLE_ALL_MASK); 2928c2ecf20Sopenharmony_ci /* enable level shifters from PS to PL */ 2938c2ecf20Sopenharmony_ci regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET, 2948c2ecf20Sopenharmony_ci LVL_SHFTR_ENABLE_PS_TO_PL); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* create a rising edge on PCFG_INIT. PCFG_INIT follows 2978c2ecf20Sopenharmony_ci * PCFG_PROG_B, so we need to poll it after setting PCFG_PROG_B 2988c2ecf20Sopenharmony_ci * to make sure the rising edge actually happens. 2998c2ecf20Sopenharmony_ci * Note: PCFG_PROG_B is low active, sequence as described in 3008c2ecf20Sopenharmony_ci * UG585 v1.10 page 211 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, CTRL_OFFSET); 3038c2ecf20Sopenharmony_ci ctrl |= CTRL_PCFG_PROG_B_MASK; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, ctrl); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status, 3088c2ecf20Sopenharmony_ci status & STATUS_PCFG_INIT_MASK, 3098c2ecf20Sopenharmony_ci INIT_POLL_DELAY, 3108c2ecf20Sopenharmony_ci INIT_POLL_TIMEOUT); 3118c2ecf20Sopenharmony_ci if (err) { 3128c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n"); 3138c2ecf20Sopenharmony_ci goto out_err; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, CTRL_OFFSET); 3178c2ecf20Sopenharmony_ci ctrl &= ~CTRL_PCFG_PROG_B_MASK; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, ctrl); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status, 3228c2ecf20Sopenharmony_ci !(status & STATUS_PCFG_INIT_MASK), 3238c2ecf20Sopenharmony_ci INIT_POLL_DELAY, 3248c2ecf20Sopenharmony_ci INIT_POLL_TIMEOUT); 3258c2ecf20Sopenharmony_ci if (err) { 3268c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Timeout waiting for !PCFG_INIT\n"); 3278c2ecf20Sopenharmony_ci goto out_err; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, CTRL_OFFSET); 3318c2ecf20Sopenharmony_ci ctrl |= CTRL_PCFG_PROG_B_MASK; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, ctrl); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status, 3368c2ecf20Sopenharmony_ci status & STATUS_PCFG_INIT_MASK, 3378c2ecf20Sopenharmony_ci INIT_POLL_DELAY, 3388c2ecf20Sopenharmony_ci INIT_POLL_TIMEOUT); 3398c2ecf20Sopenharmony_ci if (err) { 3408c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n"); 3418c2ecf20Sopenharmony_ci goto out_err; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* set configuration register with following options: 3468c2ecf20Sopenharmony_ci * - enable PCAP interface 3478c2ecf20Sopenharmony_ci * - set throughput for maximum speed (if bistream not crypted) 3488c2ecf20Sopenharmony_ci * - set CPU in user mode 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, CTRL_OFFSET); 3518c2ecf20Sopenharmony_ci if (info->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) 3528c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, 3538c2ecf20Sopenharmony_ci (CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK 3548c2ecf20Sopenharmony_ci | CTRL_PCAP_RATE_EN_MASK | ctrl)); 3558c2ecf20Sopenharmony_ci else 3568c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, 3578c2ecf20Sopenharmony_ci (CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK 3588c2ecf20Sopenharmony_ci | ctrl)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* We expect that the command queue is empty right now. */ 3628c2ecf20Sopenharmony_ci status = zynq_fpga_read(priv, STATUS_OFFSET); 3638c2ecf20Sopenharmony_ci if ((status & STATUS_DMA_Q_F) || 3648c2ecf20Sopenharmony_ci (status & STATUS_DMA_Q_E) != STATUS_DMA_Q_E) { 3658c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "DMA command queue not right\n"); 3668c2ecf20Sopenharmony_ci err = -EBUSY; 3678c2ecf20Sopenharmony_ci goto out_err; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* ensure internal PCAP loopback is disabled */ 3718c2ecf20Sopenharmony_ci ctrl = zynq_fpga_read(priv, MCTRL_OFFSET); 3728c2ecf20Sopenharmony_ci zynq_fpga_write(priv, MCTRL_OFFSET, (~MCTRL_PCAP_LPBK_MASK & ctrl)); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci clk_disable(priv->clk); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciout_err: 3798c2ecf20Sopenharmony_ci clk_disable(priv->clk); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return err; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv; 3878c2ecf20Sopenharmony_ci const char *why; 3888c2ecf20Sopenharmony_ci int err; 3898c2ecf20Sopenharmony_ci u32 intr_status; 3908c2ecf20Sopenharmony_ci unsigned long timeout; 3918c2ecf20Sopenharmony_ci unsigned long flags; 3928c2ecf20Sopenharmony_ci struct scatterlist *sg; 3938c2ecf20Sopenharmony_ci int i; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci priv = mgr->priv; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* The hardware can only DMA multiples of 4 bytes, and it requires the 3988c2ecf20Sopenharmony_ci * starting addresses to be aligned to 64 bits (UG585 pg 212). 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci for_each_sg(sgt->sgl, sg, sgt->nents, i) { 4018c2ecf20Sopenharmony_ci if ((sg->offset % 8) || (sg->length % 4)) { 4028c2ecf20Sopenharmony_ci dev_err(&mgr->dev, 4038c2ecf20Sopenharmony_ci "Invalid bitstream, chunks must be aligned\n"); 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci priv->dma_nelms = 4098c2ecf20Sopenharmony_ci dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); 4108c2ecf20Sopenharmony_ci if (priv->dma_nelms == 0) { 4118c2ecf20Sopenharmony_ci dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n"); 4128c2ecf20Sopenharmony_ci return -ENOMEM; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* enable clock */ 4168c2ecf20Sopenharmony_ci err = clk_enable(priv->clk); 4178c2ecf20Sopenharmony_ci if (err) 4188c2ecf20Sopenharmony_ci goto out_free; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK); 4218c2ecf20Sopenharmony_ci reinit_completion(&priv->dma_done); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* zynq_step_dma will turn on interrupts */ 4248c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->dma_lock, flags); 4258c2ecf20Sopenharmony_ci priv->dma_elm = 0; 4268c2ecf20Sopenharmony_ci priv->cur_sg = sgt->sgl; 4278c2ecf20Sopenharmony_ci zynq_step_dma(priv); 4288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->dma_lock, flags); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout(&priv->dma_done, 4318c2ecf20Sopenharmony_ci msecs_to_jiffies(DMA_TIMEOUT_MS)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->dma_lock, flags); 4348c2ecf20Sopenharmony_ci zynq_fpga_set_irq(priv, 0); 4358c2ecf20Sopenharmony_ci priv->cur_sg = NULL; 4368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->dma_lock, flags); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci intr_status = zynq_fpga_read(priv, INT_STS_OFFSET); 4398c2ecf20Sopenharmony_ci zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* There doesn't seem to be a way to force cancel any DMA, so if 4428c2ecf20Sopenharmony_ci * something went wrong we are relying on the hardware to have halted 4438c2ecf20Sopenharmony_ci * the DMA before we get here, if there was we could use 4448c2ecf20Sopenharmony_ci * wait_for_completion_interruptible too. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (intr_status & IXR_ERROR_FLAGS_MASK) { 4488c2ecf20Sopenharmony_ci why = "DMA reported error"; 4498c2ecf20Sopenharmony_ci err = -EIO; 4508c2ecf20Sopenharmony_ci goto out_report; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (priv->cur_sg || 4548c2ecf20Sopenharmony_ci !((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) { 4558c2ecf20Sopenharmony_ci if (timeout == 0) 4568c2ecf20Sopenharmony_ci why = "DMA timed out"; 4578c2ecf20Sopenharmony_ci else 4588c2ecf20Sopenharmony_ci why = "DMA did not complete"; 4598c2ecf20Sopenharmony_ci err = -EIO; 4608c2ecf20Sopenharmony_ci goto out_report; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci err = 0; 4648c2ecf20Sopenharmony_ci goto out_clk; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ciout_report: 4678c2ecf20Sopenharmony_ci dev_err(&mgr->dev, 4688c2ecf20Sopenharmony_ci "%s: INT_STS:0x%x CTRL:0x%x LOCK:0x%x INT_MASK:0x%x STATUS:0x%x MCTRL:0x%x\n", 4698c2ecf20Sopenharmony_ci why, 4708c2ecf20Sopenharmony_ci intr_status, 4718c2ecf20Sopenharmony_ci zynq_fpga_read(priv, CTRL_OFFSET), 4728c2ecf20Sopenharmony_ci zynq_fpga_read(priv, LOCK_OFFSET), 4738c2ecf20Sopenharmony_ci zynq_fpga_read(priv, INT_MASK_OFFSET), 4748c2ecf20Sopenharmony_ci zynq_fpga_read(priv, STATUS_OFFSET), 4758c2ecf20Sopenharmony_ci zynq_fpga_read(priv, MCTRL_OFFSET)); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciout_clk: 4788c2ecf20Sopenharmony_ci clk_disable(priv->clk); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciout_free: 4818c2ecf20Sopenharmony_ci dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); 4828c2ecf20Sopenharmony_ci return err; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, 4868c2ecf20Sopenharmony_ci struct fpga_image_info *info) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv = mgr->priv; 4898c2ecf20Sopenharmony_ci int err; 4908c2ecf20Sopenharmony_ci u32 intr_status; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci err = clk_enable(priv->clk); 4938c2ecf20Sopenharmony_ci if (err) 4948c2ecf20Sopenharmony_ci return err; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Release 'PR' control back to the ICAP */ 4978c2ecf20Sopenharmony_ci zynq_fpga_write(priv, CTRL_OFFSET, 4988c2ecf20Sopenharmony_ci zynq_fpga_read(priv, CTRL_OFFSET) & ~CTRL_PCAP_PR_MASK); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci err = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, intr_status, 5018c2ecf20Sopenharmony_ci intr_status & IXR_PCFG_DONE_MASK, 5028c2ecf20Sopenharmony_ci INIT_POLL_DELAY, 5038c2ecf20Sopenharmony_ci INIT_POLL_TIMEOUT); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci clk_disable(priv->clk); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (err) 5088c2ecf20Sopenharmony_ci return err; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* for the partial reconfig case we didn't touch the level shifters */ 5118c2ecf20Sopenharmony_ci if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { 5128c2ecf20Sopenharmony_ci /* enable level shifters from PL to PS */ 5138c2ecf20Sopenharmony_ci regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET, 5148c2ecf20Sopenharmony_ci LVL_SHFTR_ENABLE_PL_TO_PS); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* deassert AXI interface resets */ 5178c2ecf20Sopenharmony_ci regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET, 5188c2ecf20Sopenharmony_ci FPGA_RST_NONE_MASK); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int err; 5278c2ecf20Sopenharmony_ci u32 intr_status; 5288c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci priv = mgr->priv; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci err = clk_enable(priv->clk); 5338c2ecf20Sopenharmony_ci if (err) 5348c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_UNKNOWN; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci intr_status = zynq_fpga_read(priv, INT_STS_OFFSET); 5378c2ecf20Sopenharmony_ci clk_disable(priv->clk); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (intr_status & IXR_PCFG_DONE_MASK) 5408c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_OPERATING; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return FPGA_MGR_STATE_UNKNOWN; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic const struct fpga_manager_ops zynq_fpga_ops = { 5468c2ecf20Sopenharmony_ci .initial_header_size = 128, 5478c2ecf20Sopenharmony_ci .state = zynq_fpga_ops_state, 5488c2ecf20Sopenharmony_ci .write_init = zynq_fpga_ops_write_init, 5498c2ecf20Sopenharmony_ci .write_sg = zynq_fpga_ops_write, 5508c2ecf20Sopenharmony_ci .write_complete = zynq_fpga_ops_write_complete, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int zynq_fpga_probe(struct platform_device *pdev) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5568c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv; 5578c2ecf20Sopenharmony_ci struct fpga_manager *mgr; 5588c2ecf20Sopenharmony_ci struct resource *res; 5598c2ecf20Sopenharmony_ci int err; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5628c2ecf20Sopenharmony_ci if (!priv) 5638c2ecf20Sopenharmony_ci return -ENOMEM; 5648c2ecf20Sopenharmony_ci spin_lock_init(&priv->dma_lock); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5678c2ecf20Sopenharmony_ci priv->io_base = devm_ioremap_resource(dev, res); 5688c2ecf20Sopenharmony_ci if (IS_ERR(priv->io_base)) 5698c2ecf20Sopenharmony_ci return PTR_ERR(priv->io_base); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci priv->slcr = syscon_regmap_lookup_by_phandle(dev->of_node, 5728c2ecf20Sopenharmony_ci "syscon"); 5738c2ecf20Sopenharmony_ci if (IS_ERR(priv->slcr)) { 5748c2ecf20Sopenharmony_ci dev_err(dev, "unable to get zynq-slcr regmap\n"); 5758c2ecf20Sopenharmony_ci return PTR_ERR(priv->slcr); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci init_completion(&priv->dma_done); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci priv->irq = platform_get_irq(pdev, 0); 5818c2ecf20Sopenharmony_ci if (priv->irq < 0) 5828c2ecf20Sopenharmony_ci return priv->irq; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, "ref_clk"); 5858c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 5868c2ecf20Sopenharmony_ci if (PTR_ERR(priv->clk) != -EPROBE_DEFER) 5878c2ecf20Sopenharmony_ci dev_err(dev, "input clock not found\n"); 5888c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 5928c2ecf20Sopenharmony_ci if (err) { 5938c2ecf20Sopenharmony_ci dev_err(dev, "unable to enable clock\n"); 5948c2ecf20Sopenharmony_ci return err; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* unlock the device */ 5988c2ecf20Sopenharmony_ci zynq_fpga_write(priv, UNLOCK_OFFSET, UNLOCK_MASK); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci zynq_fpga_set_irq(priv, 0); 6018c2ecf20Sopenharmony_ci zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK); 6028c2ecf20Sopenharmony_ci err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev), 6038c2ecf20Sopenharmony_ci priv); 6048c2ecf20Sopenharmony_ci if (err) { 6058c2ecf20Sopenharmony_ci dev_err(dev, "unable to request IRQ\n"); 6068c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 6078c2ecf20Sopenharmony_ci return err; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci clk_disable(priv->clk); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", 6138c2ecf20Sopenharmony_ci &zynq_fpga_ops, priv); 6148c2ecf20Sopenharmony_ci if (!mgr) 6158c2ecf20Sopenharmony_ci return -ENOMEM; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mgr); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci err = fpga_mgr_register(mgr); 6208c2ecf20Sopenharmony_ci if (err) { 6218c2ecf20Sopenharmony_ci dev_err(dev, "unable to register FPGA manager\n"); 6228c2ecf20Sopenharmony_ci clk_unprepare(priv->clk); 6238c2ecf20Sopenharmony_ci return err; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int zynq_fpga_remove(struct platform_device *pdev) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct zynq_fpga_priv *priv; 6328c2ecf20Sopenharmony_ci struct fpga_manager *mgr; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci mgr = platform_get_drvdata(pdev); 6358c2ecf20Sopenharmony_ci priv = mgr->priv; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci fpga_mgr_unregister(mgr); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci clk_unprepare(priv->clk); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6458c2ecf20Sopenharmony_cistatic const struct of_device_id zynq_fpga_of_match[] = { 6468c2ecf20Sopenharmony_ci { .compatible = "xlnx,zynq-devcfg-1.0", }, 6478c2ecf20Sopenharmony_ci {}, 6488c2ecf20Sopenharmony_ci}; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zynq_fpga_of_match); 6518c2ecf20Sopenharmony_ci#endif 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic struct platform_driver zynq_fpga_driver = { 6548c2ecf20Sopenharmony_ci .probe = zynq_fpga_probe, 6558c2ecf20Sopenharmony_ci .remove = zynq_fpga_remove, 6568c2ecf20Sopenharmony_ci .driver = { 6578c2ecf20Sopenharmony_ci .name = "zynq_fpga_manager", 6588c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(zynq_fpga_of_match), 6598c2ecf20Sopenharmony_ci }, 6608c2ecf20Sopenharmony_ci}; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cimodule_platform_driver(zynq_fpga_driver); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>"); 6658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>"); 6668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx Zynq FPGA Manager"); 6678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 668