0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/clk.h>
0011 #include <linux/io.h>
0012 #include <linux/kernel.h>
0013 #include <linux/of_device.h>
0014 #include <linux/module.h>
0015 #include <linux/fpga/fpga-bridge.h>
0016
0017 #define CTRL_CMD_DECOUPLE BIT(0)
0018 #define CTRL_CMD_COUPLE 0
0019 #define CTRL_OFFSET 0
0020
0021 struct xlnx_config_data {
0022 const char *name;
0023 };
0024
0025 struct xlnx_pr_decoupler_data {
0026 const struct xlnx_config_data *ipconfig;
0027 void __iomem *io_base;
0028 struct clk *clk;
0029 };
0030
0031 static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
0032 u32 offset, u32 val)
0033 {
0034 writel(val, d->io_base + offset);
0035 }
0036
0037 static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
0038 u32 offset)
0039 {
0040 return readl(d->io_base + offset);
0041 }
0042
0043 static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
0044 {
0045 int err;
0046 struct xlnx_pr_decoupler_data *priv = bridge->priv;
0047
0048 err = clk_enable(priv->clk);
0049 if (err)
0050 return err;
0051
0052 if (enable)
0053 xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
0054 else
0055 xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
0056
0057 clk_disable(priv->clk);
0058
0059 return 0;
0060 }
0061
0062 static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
0063 {
0064 const struct xlnx_pr_decoupler_data *priv = bridge->priv;
0065 u32 status;
0066 int err;
0067
0068 err = clk_enable(priv->clk);
0069 if (err)
0070 return err;
0071
0072 status = readl(priv->io_base);
0073
0074 clk_disable(priv->clk);
0075
0076 return !status;
0077 }
0078
0079 static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
0080 .enable_set = xlnx_pr_decoupler_enable_set,
0081 .enable_show = xlnx_pr_decoupler_enable_show,
0082 };
0083
0084 #ifdef CONFIG_OF
0085 static const struct xlnx_config_data decoupler_config = {
0086 .name = "Xilinx PR Decoupler",
0087 };
0088
0089 static const struct xlnx_config_data shutdown_config = {
0090 .name = "Xilinx DFX AXI Shutdown Manager",
0091 };
0092
0093 static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
0094 { .compatible = "xlnx,pr-decoupler-1.00", .data = &decoupler_config },
0095 { .compatible = "xlnx,pr-decoupler", .data = &decoupler_config },
0096 { .compatible = "xlnx,dfx-axi-shutdown-manager-1.00",
0097 .data = &shutdown_config },
0098 { .compatible = "xlnx,dfx-axi-shutdown-manager",
0099 .data = &shutdown_config },
0100 {},
0101 };
0102 MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
0103 #endif
0104
0105 static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
0106 {
0107 struct device_node *np = pdev->dev.of_node;
0108 struct xlnx_pr_decoupler_data *priv;
0109 struct fpga_bridge *br;
0110 int err;
0111 struct resource *res;
0112
0113 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0114 if (!priv)
0115 return -ENOMEM;
0116
0117 if (np) {
0118 const struct of_device_id *match;
0119
0120 match = of_match_node(xlnx_pr_decoupler_of_match, np);
0121 if (match && match->data)
0122 priv->ipconfig = match->data;
0123 }
0124
0125 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0126 priv->io_base = devm_ioremap_resource(&pdev->dev, res);
0127 if (IS_ERR(priv->io_base))
0128 return PTR_ERR(priv->io_base);
0129
0130 priv->clk = devm_clk_get(&pdev->dev, "aclk");
0131 if (IS_ERR(priv->clk))
0132 return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
0133 "input clock not found\n");
0134
0135 err = clk_prepare_enable(priv->clk);
0136 if (err) {
0137 dev_err(&pdev->dev, "unable to enable clock\n");
0138 return err;
0139 }
0140
0141 clk_disable(priv->clk);
0142
0143 br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
0144 &xlnx_pr_decoupler_br_ops, priv);
0145 if (IS_ERR(br)) {
0146 err = PTR_ERR(br);
0147 dev_err(&pdev->dev, "unable to register %s",
0148 priv->ipconfig->name);
0149 goto err_clk;
0150 }
0151
0152 platform_set_drvdata(pdev, br);
0153
0154 return 0;
0155
0156 err_clk:
0157 clk_unprepare(priv->clk);
0158
0159 return err;
0160 }
0161
0162 static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
0163 {
0164 struct fpga_bridge *bridge = platform_get_drvdata(pdev);
0165 struct xlnx_pr_decoupler_data *p = bridge->priv;
0166
0167 fpga_bridge_unregister(bridge);
0168
0169 clk_unprepare(p->clk);
0170
0171 return 0;
0172 }
0173
0174 static struct platform_driver xlnx_pr_decoupler_driver = {
0175 .probe = xlnx_pr_decoupler_probe,
0176 .remove = xlnx_pr_decoupler_remove,
0177 .driver = {
0178 .name = "xlnx_pr_decoupler",
0179 .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
0180 },
0181 };
0182
0183 module_platform_driver(xlnx_pr_decoupler_driver);
0184
0185 MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
0186 MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
0187 MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
0188 MODULE_LICENSE("GPL v2");