1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017, National Instruments Corp.
4 * Copyright (c) 2017, Xilinx Inc
6 * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
10 #include <linux/clk.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/property.h>
17 #include <linux/fpga/fpga-bridge.h>
19 #define CTRL_CMD_DECOUPLE BIT(0)
20 #define CTRL_CMD_COUPLE 0
23 struct xlnx_config_data
{
27 struct xlnx_pr_decoupler_data
{
28 const struct xlnx_config_data
*ipconfig
;
29 void __iomem
*io_base
;
33 static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data
*d
,
36 writel(val
, d
->io_base
+ offset
);
39 static inline u32
xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data
*d
,
42 return readl(d
->io_base
+ offset
);
45 static int xlnx_pr_decoupler_enable_set(struct fpga_bridge
*bridge
, bool enable
)
48 struct xlnx_pr_decoupler_data
*priv
= bridge
->priv
;
50 err
= clk_enable(priv
->clk
);
55 xlnx_pr_decoupler_write(priv
, CTRL_OFFSET
, CTRL_CMD_COUPLE
);
57 xlnx_pr_decoupler_write(priv
, CTRL_OFFSET
, CTRL_CMD_DECOUPLE
);
59 clk_disable(priv
->clk
);
64 static int xlnx_pr_decoupler_enable_show(struct fpga_bridge
*bridge
)
66 const struct xlnx_pr_decoupler_data
*priv
= bridge
->priv
;
70 err
= clk_enable(priv
->clk
);
74 status
= xlnx_pr_decouple_read(priv
, CTRL_OFFSET
);
76 clk_disable(priv
->clk
);
81 static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops
= {
82 .enable_set
= xlnx_pr_decoupler_enable_set
,
83 .enable_show
= xlnx_pr_decoupler_enable_show
,
86 static const struct xlnx_config_data decoupler_config
= {
87 .name
= "Xilinx PR Decoupler",
90 static const struct xlnx_config_data shutdown_config
= {
91 .name
= "Xilinx DFX AXI Shutdown Manager",
94 static const struct of_device_id xlnx_pr_decoupler_of_match
[] = {
95 { .compatible
= "xlnx,pr-decoupler-1.00", .data
= &decoupler_config
},
96 { .compatible
= "xlnx,pr-decoupler", .data
= &decoupler_config
},
97 { .compatible
= "xlnx,dfx-axi-shutdown-manager-1.00",
98 .data
= &shutdown_config
},
99 { .compatible
= "xlnx,dfx-axi-shutdown-manager",
100 .data
= &shutdown_config
},
103 MODULE_DEVICE_TABLE(of
, xlnx_pr_decoupler_of_match
);
105 static int xlnx_pr_decoupler_probe(struct platform_device
*pdev
)
107 struct xlnx_pr_decoupler_data
*priv
;
108 struct fpga_bridge
*br
;
111 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
115 priv
->ipconfig
= device_get_match_data(&pdev
->dev
);
117 priv
->io_base
= devm_platform_ioremap_resource(pdev
, 0);
118 if (IS_ERR(priv
->io_base
))
119 return PTR_ERR(priv
->io_base
);
121 priv
->clk
= devm_clk_get(&pdev
->dev
, "aclk");
122 if (IS_ERR(priv
->clk
))
123 return dev_err_probe(&pdev
->dev
, PTR_ERR(priv
->clk
),
124 "input clock not found\n");
126 err
= clk_prepare_enable(priv
->clk
);
128 dev_err(&pdev
->dev
, "unable to enable clock\n");
132 clk_disable(priv
->clk
);
134 br
= fpga_bridge_register(&pdev
->dev
, priv
->ipconfig
->name
,
135 &xlnx_pr_decoupler_br_ops
, priv
);
138 dev_err(&pdev
->dev
, "unable to register %s",
139 priv
->ipconfig
->name
);
143 platform_set_drvdata(pdev
, br
);
148 clk_unprepare(priv
->clk
);
153 static void xlnx_pr_decoupler_remove(struct platform_device
*pdev
)
155 struct fpga_bridge
*bridge
= platform_get_drvdata(pdev
);
156 struct xlnx_pr_decoupler_data
*p
= bridge
->priv
;
158 fpga_bridge_unregister(bridge
);
160 clk_unprepare(p
->clk
);
163 static struct platform_driver xlnx_pr_decoupler_driver
= {
164 .probe
= xlnx_pr_decoupler_probe
,
165 .remove
= xlnx_pr_decoupler_remove
,
167 .name
= "xlnx_pr_decoupler",
168 .of_match_table
= xlnx_pr_decoupler_of_match
,
172 module_platform_driver(xlnx_pr_decoupler_driver
);
174 MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
175 MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
176 MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
177 MODULE_LICENSE("GPL v2");