linux/drivers/fpga/xilinx-pr-decoupler.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2017, National Instruments Corp.
   4 * Copyright (c) 2017, Xilinx Inc
   5 *
   6 * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
   7 * Decoupler IP Core.
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/io.h>
  12#include <linux/kernel.h>
  13#include <linux/of_device.h>
  14#include <linux/module.h>
  15#include <linux/fpga/fpga-bridge.h>
  16
  17#define CTRL_CMD_DECOUPLE       BIT(0)
  18#define CTRL_CMD_COUPLE         0
  19#define CTRL_OFFSET             0
  20
  21struct xlnx_config_data {
  22        const char *name;
  23};
  24
  25struct xlnx_pr_decoupler_data {
  26        const struct xlnx_config_data *ipconfig;
  27        void __iomem *io_base;
  28        struct clk *clk;
  29};
  30
  31static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
  32                                           u32 offset, u32 val)
  33{
  34        writel(val, d->io_base + offset);
  35}
  36
  37static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
  38                                        u32 offset)
  39{
  40        return readl(d->io_base + offset);
  41}
  42
  43static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
  44{
  45        int err;
  46        struct xlnx_pr_decoupler_data *priv = bridge->priv;
  47
  48        err = clk_enable(priv->clk);
  49        if (err)
  50                return err;
  51
  52        if (enable)
  53                xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
  54        else
  55                xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
  56
  57        clk_disable(priv->clk);
  58
  59        return 0;
  60}
  61
  62static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
  63{
  64        const struct xlnx_pr_decoupler_data *priv = bridge->priv;
  65        u32 status;
  66        int err;
  67
  68        err = clk_enable(priv->clk);
  69        if (err)
  70                return err;
  71
  72        status = readl(priv->io_base);
  73
  74        clk_disable(priv->clk);
  75
  76        return !status;
  77}
  78
  79static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
  80        .enable_set = xlnx_pr_decoupler_enable_set,
  81        .enable_show = xlnx_pr_decoupler_enable_show,
  82};
  83
  84static const struct xlnx_config_data decoupler_config = {
  85        .name = "Xilinx PR Decoupler",
  86};
  87
  88static const struct xlnx_config_data shutdown_config = {
  89        .name = "Xilinx DFX AXI Shutdown Manager",
  90};
  91
  92static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
  93        { .compatible = "xlnx,pr-decoupler-1.00", .data = &decoupler_config },
  94        { .compatible = "xlnx,pr-decoupler", .data = &decoupler_config },
  95        { .compatible = "xlnx,dfx-axi-shutdown-manager-1.00",
  96                                        .data = &shutdown_config },
  97        { .compatible = "xlnx,dfx-axi-shutdown-manager",
  98                                        .data = &shutdown_config },
  99        {},
 100};
 101MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
 102
 103static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
 104{
 105        struct device_node *np = pdev->dev.of_node;
 106        struct xlnx_pr_decoupler_data *priv;
 107        struct fpga_bridge *br;
 108        int err;
 109        struct resource *res;
 110
 111        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 112        if (!priv)
 113                return -ENOMEM;
 114
 115        if (np) {
 116                const struct of_device_id *match;
 117
 118                match = of_match_node(xlnx_pr_decoupler_of_match, np);
 119                if (match && match->data)
 120                        priv->ipconfig = match->data;
 121        }
 122
 123        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 124        priv->io_base = devm_ioremap_resource(&pdev->dev, res);
 125        if (IS_ERR(priv->io_base))
 126                return PTR_ERR(priv->io_base);
 127
 128        priv->clk = devm_clk_get(&pdev->dev, "aclk");
 129        if (IS_ERR(priv->clk))
 130                return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
 131                                     "input clock not found\n");
 132
 133        err = clk_prepare_enable(priv->clk);
 134        if (err) {
 135                dev_err(&pdev->dev, "unable to enable clock\n");
 136                return err;
 137        }
 138
 139        clk_disable(priv->clk);
 140
 141        br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
 142                                     &xlnx_pr_decoupler_br_ops, priv);
 143        if (!br) {
 144                err = -ENOMEM;
 145                goto err_clk;
 146        }
 147
 148        platform_set_drvdata(pdev, br);
 149
 150        err = fpga_bridge_register(br);
 151        if (err) {
 152                dev_err(&pdev->dev, "unable to register %s",
 153                        priv->ipconfig->name);
 154                goto err_clk;
 155        }
 156
 157        return 0;
 158
 159err_clk:
 160        clk_unprepare(priv->clk);
 161
 162        return err;
 163}
 164
 165static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
 166{
 167        struct fpga_bridge *bridge = platform_get_drvdata(pdev);
 168        struct xlnx_pr_decoupler_data *p = bridge->priv;
 169
 170        fpga_bridge_unregister(bridge);
 171
 172        clk_unprepare(p->clk);
 173
 174        return 0;
 175}
 176
 177static struct platform_driver xlnx_pr_decoupler_driver = {
 178        .probe = xlnx_pr_decoupler_probe,
 179        .remove = xlnx_pr_decoupler_remove,
 180        .driver = {
 181                .name = "xlnx_pr_decoupler",
 182                .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
 183        },
 184};
 185
 186module_platform_driver(xlnx_pr_decoupler_driver);
 187
 188MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
 189MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
 190MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
 191MODULE_LICENSE("GPL v2");
 192