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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | /* * Copyright (c) 2017, National Instruments Corp. * Copyright (c) 2017, Xilix Inc * * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration * Decoupler IP Core. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/clk.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/of_device.h> #include <linux/module.h> #include <linux/fpga/fpga-bridge.h> #define CTRL_CMD_DECOUPLE BIT(0) #define CTRL_CMD_COUPLE 0 #define CTRL_OFFSET 0 struct xlnx_pr_decoupler_data { void __iomem *io_base; struct clk *clk; }; static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d, u32 offset, u32 val) { writel(val, d->io_base + offset); } static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d, u32 offset) { return readl(d->io_base + offset); } static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable) { int err; struct xlnx_pr_decoupler_data *priv = bridge->priv; err = clk_enable(priv->clk); if (err) return err; if (enable) xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE); else xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE); clk_disable(priv->clk); return 0; } static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge) { const struct xlnx_pr_decoupler_data *priv = bridge->priv; u32 status; int err; err = clk_enable(priv->clk); if (err) return err; status = readl(priv->io_base); clk_disable(priv->clk); return !status; } static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = { .enable_set = xlnx_pr_decoupler_enable_set, .enable_show = xlnx_pr_decoupler_enable_show, }; static const struct of_device_id xlnx_pr_decoupler_of_match[] = { { .compatible = "xlnx,pr-decoupler-1.00", }, { .compatible = "xlnx,pr-decoupler", }, {}, }; MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match); static int xlnx_pr_decoupler_probe(struct platform_device *pdev) { struct xlnx_pr_decoupler_data *priv; struct fpga_bridge *br; int err; struct resource *res; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->io_base)) return PTR_ERR(priv->io_base); priv->clk = devm_clk_get(&pdev->dev, "aclk"); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "input clock not found\n"); return PTR_ERR(priv->clk); } err = clk_prepare_enable(priv->clk); if (err) { dev_err(&pdev->dev, "unable to enable clock\n"); return err; } clk_disable(priv->clk); br = fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", &xlnx_pr_decoupler_br_ops, priv); if (!br) { err = -ENOMEM; goto err_clk; } platform_set_drvdata(pdev, br); err = fpga_bridge_register(br); if (err) { dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler"); goto err_clk; } return 0; err_clk: clk_unprepare(priv->clk); return err; } static int xlnx_pr_decoupler_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); struct xlnx_pr_decoupler_data *p = bridge->priv; fpga_bridge_unregister(bridge); clk_unprepare(p->clk); return 0; } static struct platform_driver xlnx_pr_decoupler_driver = { .probe = xlnx_pr_decoupler_probe, .remove = xlnx_pr_decoupler_remove, .driver = { .name = "xlnx_pr_decoupler", .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match), }, }; module_platform_driver(xlnx_pr_decoupler_driver); MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler"); MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>"); MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>"); MODULE_LICENSE("GPL v2"); |