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