18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27e961c12SMoritz Fischer /*
37e961c12SMoritz Fischer * Copyright (c) 2017, National Instruments Corp.
430a2ac9aSNava kishore Manne * Copyright (c) 2017, Xilinx Inc
57e961c12SMoritz Fischer *
67e961c12SMoritz Fischer * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
77e961c12SMoritz Fischer * Decoupler IP Core.
87e961c12SMoritz Fischer */
97e961c12SMoritz Fischer
107e961c12SMoritz Fischer #include <linux/clk.h>
117e961c12SMoritz Fischer #include <linux/io.h>
127e961c12SMoritz Fischer #include <linux/kernel.h>
137e961c12SMoritz Fischer #include <linux/of_device.h>
147e961c12SMoritz Fischer #include <linux/module.h>
157e961c12SMoritz Fischer #include <linux/fpga/fpga-bridge.h>
167e961c12SMoritz Fischer
177e961c12SMoritz Fischer #define CTRL_CMD_DECOUPLE BIT(0)
187e961c12SMoritz Fischer #define CTRL_CMD_COUPLE 0
197e961c12SMoritz Fischer #define CTRL_OFFSET 0
207e961c12SMoritz Fischer
2130a2ac9aSNava kishore Manne struct xlnx_config_data {
2230a2ac9aSNava kishore Manne const char *name;
2330a2ac9aSNava kishore Manne };
2430a2ac9aSNava kishore Manne
257e961c12SMoritz Fischer struct xlnx_pr_decoupler_data {
2630a2ac9aSNava kishore Manne const struct xlnx_config_data *ipconfig;
277e961c12SMoritz Fischer void __iomem *io_base;
287e961c12SMoritz Fischer struct clk *clk;
297e961c12SMoritz Fischer };
307e961c12SMoritz Fischer
xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data * d,u32 offset,u32 val)317e961c12SMoritz Fischer static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
327e961c12SMoritz Fischer u32 offset, u32 val)
337e961c12SMoritz Fischer {
347e961c12SMoritz Fischer writel(val, d->io_base + offset);
357e961c12SMoritz Fischer }
367e961c12SMoritz Fischer
xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data * d,u32 offset)377e961c12SMoritz Fischer static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
387e961c12SMoritz Fischer u32 offset)
397e961c12SMoritz Fischer {
407e961c12SMoritz Fischer return readl(d->io_base + offset);
417e961c12SMoritz Fischer }
427e961c12SMoritz Fischer
xlnx_pr_decoupler_enable_set(struct fpga_bridge * bridge,bool enable)437e961c12SMoritz Fischer static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
447e961c12SMoritz Fischer {
457e961c12SMoritz Fischer int err;
467e961c12SMoritz Fischer struct xlnx_pr_decoupler_data *priv = bridge->priv;
477e961c12SMoritz Fischer
487e961c12SMoritz Fischer err = clk_enable(priv->clk);
497e961c12SMoritz Fischer if (err)
507e961c12SMoritz Fischer return err;
517e961c12SMoritz Fischer
527e961c12SMoritz Fischer if (enable)
537e961c12SMoritz Fischer xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
547e961c12SMoritz Fischer else
557e961c12SMoritz Fischer xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
567e961c12SMoritz Fischer
577e961c12SMoritz Fischer clk_disable(priv->clk);
587e961c12SMoritz Fischer
597e961c12SMoritz Fischer return 0;
607e961c12SMoritz Fischer }
617e961c12SMoritz Fischer
xlnx_pr_decoupler_enable_show(struct fpga_bridge * bridge)627e961c12SMoritz Fischer static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
637e961c12SMoritz Fischer {
647e961c12SMoritz Fischer const struct xlnx_pr_decoupler_data *priv = bridge->priv;
657e961c12SMoritz Fischer u32 status;
667e961c12SMoritz Fischer int err;
677e961c12SMoritz Fischer
687e961c12SMoritz Fischer err = clk_enable(priv->clk);
697e961c12SMoritz Fischer if (err)
707e961c12SMoritz Fischer return err;
717e961c12SMoritz Fischer
72d2b727cbSMichal Simek status = xlnx_pr_decouple_read(priv, CTRL_OFFSET);
737e961c12SMoritz Fischer
747e961c12SMoritz Fischer clk_disable(priv->clk);
757e961c12SMoritz Fischer
767e961c12SMoritz Fischer return !status;
777e961c12SMoritz Fischer }
787e961c12SMoritz Fischer
79d8d9d936SBhumika Goyal static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
807e961c12SMoritz Fischer .enable_set = xlnx_pr_decoupler_enable_set,
817e961c12SMoritz Fischer .enable_show = xlnx_pr_decoupler_enable_show,
827e961c12SMoritz Fischer };
837e961c12SMoritz Fischer
8456ddc787SMoritz Fischer #ifdef CONFIG_OF
8530a2ac9aSNava kishore Manne static const struct xlnx_config_data decoupler_config = {
8630a2ac9aSNava kishore Manne .name = "Xilinx PR Decoupler",
8730a2ac9aSNava kishore Manne };
8830a2ac9aSNava kishore Manne
8930a2ac9aSNava kishore Manne static const struct xlnx_config_data shutdown_config = {
9030a2ac9aSNava kishore Manne .name = "Xilinx DFX AXI Shutdown Manager",
9130a2ac9aSNava kishore Manne };
9230a2ac9aSNava kishore Manne
937e961c12SMoritz Fischer static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
9430a2ac9aSNava kishore Manne { .compatible = "xlnx,pr-decoupler-1.00", .data = &decoupler_config },
9530a2ac9aSNava kishore Manne { .compatible = "xlnx,pr-decoupler", .data = &decoupler_config },
9630a2ac9aSNava kishore Manne { .compatible = "xlnx,dfx-axi-shutdown-manager-1.00",
9730a2ac9aSNava kishore Manne .data = &shutdown_config },
9830a2ac9aSNava kishore Manne { .compatible = "xlnx,dfx-axi-shutdown-manager",
9930a2ac9aSNava kishore Manne .data = &shutdown_config },
1007e961c12SMoritz Fischer {},
1017e961c12SMoritz Fischer };
1027e961c12SMoritz Fischer MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
10356ddc787SMoritz Fischer #endif
1047e961c12SMoritz Fischer
xlnx_pr_decoupler_probe(struct platform_device * pdev)1057e961c12SMoritz Fischer static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
1067e961c12SMoritz Fischer {
10730a2ac9aSNava kishore Manne struct device_node *np = pdev->dev.of_node;
1087e961c12SMoritz Fischer struct xlnx_pr_decoupler_data *priv;
109371cd1b1SAlan Tull struct fpga_bridge *br;
1107e961c12SMoritz Fischer int err;
1117e961c12SMoritz Fischer
1127e961c12SMoritz Fischer priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1137e961c12SMoritz Fischer if (!priv)
1147e961c12SMoritz Fischer return -ENOMEM;
1157e961c12SMoritz Fischer
11630a2ac9aSNava kishore Manne if (np) {
11730a2ac9aSNava kishore Manne const struct of_device_id *match;
11830a2ac9aSNava kishore Manne
11930a2ac9aSNava kishore Manne match = of_match_node(xlnx_pr_decoupler_of_match, np);
12030a2ac9aSNava kishore Manne if (match && match->data)
12130a2ac9aSNava kishore Manne priv->ipconfig = match->data;
12230a2ac9aSNava kishore Manne }
12330a2ac9aSNava kishore Manne
124*ebe00825SYangtao Li priv->io_base = devm_platform_ioremap_resource(pdev, 0);
1257e961c12SMoritz Fischer if (IS_ERR(priv->io_base))
1267e961c12SMoritz Fischer return PTR_ERR(priv->io_base);
1277e961c12SMoritz Fischer
1287e961c12SMoritz Fischer priv->clk = devm_clk_get(&pdev->dev, "aclk");
129b5c6ecfdSMichal Simek if (IS_ERR(priv->clk))
130b5c6ecfdSMichal Simek return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
131b5c6ecfdSMichal Simek "input clock not found\n");
1327e961c12SMoritz Fischer
1337e961c12SMoritz Fischer err = clk_prepare_enable(priv->clk);
1347e961c12SMoritz Fischer if (err) {
1357e961c12SMoritz Fischer dev_err(&pdev->dev, "unable to enable clock\n");
1367e961c12SMoritz Fischer return err;
1377e961c12SMoritz Fischer }
1387e961c12SMoritz Fischer
1397e961c12SMoritz Fischer clk_disable(priv->clk);
1407e961c12SMoritz Fischer
1410d70af3cSRuss Weight br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
1427e961c12SMoritz Fischer &xlnx_pr_decoupler_br_ops, priv);
1430d70af3cSRuss Weight if (IS_ERR(br)) {
1440d70af3cSRuss Weight err = PTR_ERR(br);
14530a2ac9aSNava kishore Manne dev_err(&pdev->dev, "unable to register %s",
14630a2ac9aSNava kishore Manne priv->ipconfig->name);
147371cd1b1SAlan Tull goto err_clk;
1487e961c12SMoritz Fischer }
1497e961c12SMoritz Fischer
1500d70af3cSRuss Weight platform_set_drvdata(pdev, br);
1510d70af3cSRuss Weight
1527e961c12SMoritz Fischer return 0;
153371cd1b1SAlan Tull
154371cd1b1SAlan Tull err_clk:
155371cd1b1SAlan Tull clk_unprepare(priv->clk);
156371cd1b1SAlan Tull
157371cd1b1SAlan Tull return err;
1587e961c12SMoritz Fischer }
1597e961c12SMoritz Fischer
xlnx_pr_decoupler_remove(struct platform_device * pdev)1607e961c12SMoritz Fischer static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
1617e961c12SMoritz Fischer {
1627e961c12SMoritz Fischer struct fpga_bridge *bridge = platform_get_drvdata(pdev);
1637e961c12SMoritz Fischer struct xlnx_pr_decoupler_data *p = bridge->priv;
1647e961c12SMoritz Fischer
165371cd1b1SAlan Tull fpga_bridge_unregister(bridge);
1667e961c12SMoritz Fischer
1677e961c12SMoritz Fischer clk_unprepare(p->clk);
1687e961c12SMoritz Fischer
1697e961c12SMoritz Fischer return 0;
1707e961c12SMoritz Fischer }
1717e961c12SMoritz Fischer
1727e961c12SMoritz Fischer static struct platform_driver xlnx_pr_decoupler_driver = {
1737e961c12SMoritz Fischer .probe = xlnx_pr_decoupler_probe,
1747e961c12SMoritz Fischer .remove = xlnx_pr_decoupler_remove,
1757e961c12SMoritz Fischer .driver = {
1767e961c12SMoritz Fischer .name = "xlnx_pr_decoupler",
1777e961c12SMoritz Fischer .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
1787e961c12SMoritz Fischer },
1797e961c12SMoritz Fischer };
1807e961c12SMoritz Fischer
1817e961c12SMoritz Fischer module_platform_driver(xlnx_pr_decoupler_driver);
1827e961c12SMoritz Fischer
1837e961c12SMoritz Fischer MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
1847e961c12SMoritz Fischer MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
1857e961c12SMoritz Fischer MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
1867e961c12SMoritz Fischer MODULE_LICENSE("GPL v2");
187