Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2017, National Instruments Corp.
0004  * Copyright (c) 2017, Xilinx Inc
0005  *
0006  * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
0007  * Decoupler IP Core.
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");