xilinx-pr-decoupler.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. #include <linux/clk.h>
  10. #include <linux/io.h>
  11. #include <linux/kernel.h>
  12. #include <linux/of_device.h>
  13. #include <linux/module.h>
  14. #include <linux/fpga/fpga-bridge.h>
  15. #define CTRL_CMD_DECOUPLE BIT(0)
  16. #define CTRL_CMD_COUPLE 0
  17. #define CTRL_OFFSET 0
  18. struct xlnx_config_data {
  19. const char *name;
  20. };
  21. struct xlnx_pr_decoupler_data {
  22. const struct xlnx_config_data *ipconfig;
  23. void __iomem *io_base;
  24. struct clk *clk;
  25. };
  26. static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
  27. u32 offset, u32 val)
  28. {
  29. writel(val, d->io_base + offset);
  30. }
  31. static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
  32. u32 offset)
  33. {
  34. return readl(d->io_base + offset);
  35. }
  36. static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
  37. {
  38. int err;
  39. struct xlnx_pr_decoupler_data *priv = bridge->priv;
  40. err = clk_enable(priv->clk);
  41. if (err)
  42. return err;
  43. if (enable)
  44. xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
  45. else
  46. xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
  47. clk_disable(priv->clk);
  48. return 0;
  49. }
  50. static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
  51. {
  52. const struct xlnx_pr_decoupler_data *priv = bridge->priv;
  53. u32 status;
  54. int err;
  55. err = clk_enable(priv->clk);
  56. if (err)
  57. return err;
  58. status = readl(priv->io_base);
  59. clk_disable(priv->clk);
  60. return !status;
  61. }
  62. static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
  63. .enable_set = xlnx_pr_decoupler_enable_set,
  64. .enable_show = xlnx_pr_decoupler_enable_show,
  65. };
  66. #ifdef CONFIG_OF
  67. static const struct xlnx_config_data decoupler_config = {
  68. .name = "Xilinx PR Decoupler",
  69. };
  70. static const struct xlnx_config_data shutdown_config = {
  71. .name = "Xilinx DFX AXI Shutdown Manager",
  72. };
  73. static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
  74. { .compatible = "xlnx,pr-decoupler-1.00", .data = &decoupler_config },
  75. { .compatible = "xlnx,pr-decoupler", .data = &decoupler_config },
  76. { .compatible = "xlnx,dfx-axi-shutdown-manager-1.00",
  77. .data = &shutdown_config },
  78. { .compatible = "xlnx,dfx-axi-shutdown-manager",
  79. .data = &shutdown_config },
  80. {},
  81. };
  82. MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
  83. #endif
  84. static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
  85. {
  86. struct device_node *np = pdev->dev.of_node;
  87. struct xlnx_pr_decoupler_data *priv;
  88. struct fpga_bridge *br;
  89. int err;
  90. struct resource *res;
  91. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  92. if (!priv)
  93. return -ENOMEM;
  94. if (np) {
  95. const struct of_device_id *match;
  96. match = of_match_node(xlnx_pr_decoupler_of_match, np);
  97. if (match && match->data)
  98. priv->ipconfig = match->data;
  99. }
  100. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  101. priv->io_base = devm_ioremap_resource(&pdev->dev, res);
  102. if (IS_ERR(priv->io_base))
  103. return PTR_ERR(priv->io_base);
  104. priv->clk = devm_clk_get(&pdev->dev, "aclk");
  105. if (IS_ERR(priv->clk))
  106. return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
  107. "input clock not found\n");
  108. err = clk_prepare_enable(priv->clk);
  109. if (err) {
  110. dev_err(&pdev->dev, "unable to enable clock\n");
  111. return err;
  112. }
  113. clk_disable(priv->clk);
  114. br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
  115. &xlnx_pr_decoupler_br_ops, priv);
  116. if (IS_ERR(br)) {
  117. err = PTR_ERR(br);
  118. dev_err(&pdev->dev, "unable to register %s",
  119. priv->ipconfig->name);
  120. goto err_clk;
  121. }
  122. platform_set_drvdata(pdev, br);
  123. return 0;
  124. err_clk:
  125. clk_unprepare(priv->clk);
  126. return err;
  127. }
  128. static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
  129. {
  130. struct fpga_bridge *bridge = platform_get_drvdata(pdev);
  131. struct xlnx_pr_decoupler_data *p = bridge->priv;
  132. fpga_bridge_unregister(bridge);
  133. clk_unprepare(p->clk);
  134. return 0;
  135. }
  136. static struct platform_driver xlnx_pr_decoupler_driver = {
  137. .probe = xlnx_pr_decoupler_probe,
  138. .remove = xlnx_pr_decoupler_remove,
  139. .driver = {
  140. .name = "xlnx_pr_decoupler",
  141. .of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
  142. },
  143. };
  144. module_platform_driver(xlnx_pr_decoupler_driver);
  145. MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
  146. MODULE_AUTHOR("Moritz Fischer <[email protected]>");
  147. MODULE_AUTHOR("Michal Simek <[email protected]>");
  148. MODULE_LICENSE("GPL v2");