aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/fpga
diff options
context:
space:
mode:
authorMoritz Fischer <mdf@kernel.org>2017-03-24 11:33:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 11:50:36 -0400
commit7e961c12be424c6c1e355d469cc1b82dbf3af718 (patch)
treeea4635753df73afbab91b111a1dd8050572f33c7 /drivers/fpga
parent00846a4425d373761c70cbe82cceb98d3d4da179 (diff)
fpga: Add support for Xilinx LogiCORE PR Decoupler
This adds support for the Xilinx LogiCORE PR Decoupler soft-ip that does decoupling of PR regions in the FPGA fabric during partial reconfiguration. Signed-off-by: Moritz Fischer <mdf@kernel.org> Signed-off-by: Michal Simek <michal.simek@xilinx.com> Cc: Sören Brinkmann <soren.brinkmann@xilinx.com> Cc: linux-kernel@vger.kernel.org Cc: devicetree@vger.kernel.org Acked-by: Alan Tull <atull@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga')
-rw-r--r--drivers/fpga/Kconfig10
-rw-r--r--drivers/fpga/Makefile1
-rw-r--r--drivers/fpga/xilinx-pr-decoupler.c161
3 files changed, 172 insertions, 0 deletions
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 116ee92fe034..161ba9dccede 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -95,6 +95,16 @@ config ALTERA_PR_IP_CORE_PLAT
95 Platform driver support for Altera Partial Reconfiguration IP 95 Platform driver support for Altera Partial Reconfiguration IP
96 component 96 component
97 97
98config XILINX_PR_DECOUPLER
99 tristate "Xilinx LogiCORE PR Decoupler"
100 depends on FPGA_BRIDGE
101 depends on HAS_IOMEM
102 help
103 Say Y to enable drivers for Xilinx LogiCORE PR Decoupler.
104 The PR Decoupler exists in the FPGA fabric to isolate one
105 region of the FPGA from the busses while that region is
106 being reprogrammed during partial reconfig.
107
98endif # FPGA 108endif # FPGA
99 109
100endmenu 110endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 530cf9410dde..2a4f0218145c 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT) += altera-pr-ip-core-plat.o
19obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o 19obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
20obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o 20obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o
21obj-$(CONFIG_ALTERA_FREEZE_BRIDGE) += altera-freeze-bridge.o 21obj-$(CONFIG_ALTERA_FREEZE_BRIDGE) += altera-freeze-bridge.o
22obj-$(CONFIG_XILINX_PR_DECOUPLER) += xilinx-pr-decoupler.o
22 23
23# High Level Interfaces 24# High Level Interfaces
24obj-$(CONFIG_FPGA_REGION) += fpga-region.o 25obj-$(CONFIG_FPGA_REGION) += fpga-region.o
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
new file mode 100644
index 000000000000..e359930bebc8
--- /dev/null
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -0,0 +1,161 @@
1/*
2 * Copyright (c) 2017, National Instruments Corp.
3 * Copyright (c) 2017, Xilix Inc
4 *
5 * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
6 * Decoupler IP Core.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <linux/clk.h>
19#include <linux/io.h>
20#include <linux/kernel.h>
21#include <linux/of_device.h>
22#include <linux/module.h>
23#include <linux/fpga/fpga-bridge.h>
24
25#define CTRL_CMD_DECOUPLE BIT(0)
26#define CTRL_CMD_COUPLE 0
27#define CTRL_OFFSET 0
28
29struct xlnx_pr_decoupler_data {
30 void __iomem *io_base;
31 struct clk *clk;
32};
33
34static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
35 u32 offset, u32 val)
36{
37 writel(val, d->io_base + offset);
38}
39
40static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
41 u32 offset)
42{
43 return readl(d->io_base + offset);
44}
45
46static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
47{
48 int err;
49 struct xlnx_pr_decoupler_data *priv = bridge->priv;
50
51 err = clk_enable(priv->clk);
52 if (err)
53 return err;
54
55 if (enable)
56 xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
57 else
58 xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
59
60 clk_disable(priv->clk);
61
62 return 0;
63}
64
65static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
66{
67 const struct xlnx_pr_decoupler_data *priv = bridge->priv;
68 u32 status;
69 int err;
70
71 err = clk_enable(priv->clk);
72 if (err)
73 return err;
74
75 status = readl(priv->io_base);
76
77 clk_disable(priv->clk);
78
79 return !status;
80}
81
82static struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
83 .enable_set = xlnx_pr_decoupler_enable_set,
84 .enable_show = xlnx_pr_decoupler_enable_show,
85};
86
87static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
88 { .compatible = "xlnx,pr-decoupler-1.00", },
89 { .compatible = "xlnx,pr-decoupler", },
90 {},
91};
92MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
93
94static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
95{
96 struct xlnx_pr_decoupler_data *priv;
97 int err;
98 struct resource *res;
99
100 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
101 if (!priv)
102 return -ENOMEM;
103
104 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
105 priv->io_base = devm_ioremap_resource(&pdev->dev, res);
106 if (IS_ERR(priv->io_base))
107 return PTR_ERR(priv->io_base);
108
109 priv->clk = devm_clk_get(&pdev->dev, "aclk");
110 if (IS_ERR(priv->clk)) {
111 dev_err(&pdev->dev, "input clock not found\n");
112 return PTR_ERR(priv->clk);
113 }
114
115 err = clk_prepare_enable(priv->clk);
116 if (err) {
117 dev_err(&pdev->dev, "unable to enable clock\n");
118 return err;
119 }
120
121 clk_disable(priv->clk);
122
123 err = fpga_bridge_register(&pdev->dev, "Xilinx PR Decoupler",
124 &xlnx_pr_decoupler_br_ops, priv);
125
126 if (err) {
127 dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler");
128 clk_unprepare(priv->clk);
129 return err;
130 }
131
132 return 0;
133}
134
135static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
136{
137 struct fpga_bridge *bridge = platform_get_drvdata(pdev);
138 struct xlnx_pr_decoupler_data *p = bridge->priv;
139
140 fpga_bridge_unregister(&pdev->dev);
141
142 clk_unprepare(p->clk);
143
144 return 0;
145}
146
147static struct platform_driver xlnx_pr_decoupler_driver = {
148 .probe = xlnx_pr_decoupler_probe,
149 .remove = xlnx_pr_decoupler_remove,
150 .driver = {
151 .name = "xlnx_pr_decoupler",
152 .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
153 },
154};
155
156module_platform_driver(xlnx_pr_decoupler_driver);
157
158MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
159MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
160MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
161MODULE_LICENSE("GPL v2");