[PATCH 4/4] fpga: zynqmp: Add afi config driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add zynqmp AXI FIFO interface(AFI) config driver. This is useful for the
configuration of the PS-PL interface on Zynq US+ MPSoC platform.

Signed-off-by: Nava kishore Manne <nava.kishore.manne@xxxxxxx>
---
 MAINTAINERS               |   6 ++
 drivers/fpga/Kconfig      |  13 +++
 drivers/fpga/Makefile     |   1 +
 drivers/fpga/zynqmp-afi.c | 211 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 231 insertions(+)
 create mode 100644 drivers/fpga/zynqmp-afi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 20ffac651214..957e753e6406 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8032,6 +8032,12 @@ F:	Documentation/fpga/
 F:	drivers/fpga/
 F:	include/linux/fpga/
 
+FPGA ZYNQMP PS-PL BRIDGE DRIVER
+M:	Nava kishore Manne <nava.kishore.manne@xxxxxxx>
+S:	Supported
+F:	Documentation/devicetree/bindings/fpga/xlnx,zynqmp-afi-fpga.yaml
+F:	drivers/fpga/zynqmp-afi.c
+
 INTEL MAX10 BMC SECURE UPDATES
 M:	Russ Weight <russell.h.weight@xxxxxxxxx>
 L:	linux-fpga@xxxxxxxxxxxxxxx
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 6c416955da53..c08794d30fb5 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -130,6 +130,19 @@ config XILINX_PR_DECOUPLER
 	  reconfiguration, preventing the system deadlock that can
 	  occur if AXI transactions are interrupted by DFX.
 
+config ZYNQMP_AFI
+	tristate "Xilinx ZYNQMP AFI support"
+	depends on FPGA_BRIDGE
+	help
+	  Say Y to enable drivers to handle the PS-PL clocks configurations
+	  and PS-PL Bus-width. Xilinx Zynq US+ MPSoC connect the PS to the
+	  programmable logic (PL) through the AXI port. This AXI port helps
+	  to establish the data path between the PS and PL.
+	  In-order to establish the proper communication path between PS and PL,
+	  the AXI port data path should be configured with the proper Bus-width
+	  values and it will also handles the PS-PL reset signals to reset the
+	  PL domain.
+
 config FPGA_REGION
 	tristate "FPGA Region"
 	depends on FPGA_BRIDGE
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 42ae8b58abce..94cfe60972db 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
 obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
 obj-$(CONFIG_ALTERA_FREEZE_BRIDGE)	+= altera-freeze-bridge.o
 obj-$(CONFIG_XILINX_PR_DECOUPLER)	+= xilinx-pr-decoupler.o
+obj-$(CONFIG_ZYNQMP_AFI)		+= zynqmp-afi.o
 
 # High Level Interfaces
 obj-$(CONFIG_FPGA_REGION)		+= fpga-region.o
diff --git a/drivers/fpga/zynqmp-afi.c b/drivers/fpga/zynqmp-afi.c
new file mode 100644
index 000000000000..bc975d304039
--- /dev/null
+++ b/drivers/fpga/zynqmp-afi.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Xilinx, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+/* Registers and special values for doing register-based operations */
+#define AFI_RDCHAN_CTRL_OFFSET	0x00
+#define AFI_WRCHAN_CTRL_OFFSET	0x14
+#define AFI_BUSWIDTH_MASK	BIT(0)
+
+/**
+ * struct zynqmp_afi - AFI register description.
+ * @dev: device that owns this.
+ * @of_node: Device Tree overlay.
+ * @resets: Pointer to the reset control for ps-pl resets.
+ */
+struct zynqmp_afi {
+	struct device *dev;
+	struct device_node *of_node;
+	struct reset_control *resets;
+};
+
+/**
+ * struct zynqmp_afi_configreg - AFI configuration registers info.
+ * @reg:	Name of the AFI configuration register.
+ * @id:		Register index value.
+ */
+struct zynqmp_afi_configreg {
+	char *reg;
+	u32 id;
+};
+
+static struct zynqmp_afi_configreg afi_cfgreg[] = {
+	{.reg = "xlnx,afi-fm0-rd-bus-width",	.id = AFIFM0_RDCTRL},
+	{.reg = "xlnx,afi-fm1-rd-bus-width",	.id = AFIFM1_RDCTRL},
+	{.reg = "xlnx,afi-fm2-rd-bus-width",	.id = AFIFM2_RDCTRL},
+	{.reg = "xlnx,afi-fm3-rd-bus-width",	.id = AFIFM3_RDCTRL},
+	{.reg = "xlnx,afi-fm4-rd-bus-width",	.id = AFIFM4_RDCTRL},
+	{.reg = "xlnx,afi-fm5-rd-bus-width",	.id = AFIFM5_RDCTRL},
+	{.reg = "xlnx,afi-fm6-rd-bus-width",	.id = AFIFM6_RDCTRL},
+	{.reg = "xlnx,afi-fm0-wr-bus-width",	.id = AFIFM0_WRCTRL},
+	{.reg = "xlnx,afi-fm1-wr-bus-width",	.id = AFIFM1_WRCTRL},
+	{.reg = "xlnx,afi-fm2-wr-bus-width",	.id = AFIFM2_WRCTRL},
+	{.reg = "xlnx,afi-fm3-wr-bus-width",	.id = AFIFM3_WRCTRL},
+	{.reg = "xlnx,afi-fm4-wr-bus-width",	.id = AFIFM4_WRCTRL},
+	{.reg = "xlnx,afi-fm5-wr-bus-width",	.id = AFIFM5_WRCTRL},
+	{.reg = "xlnx,afi-fm6-wr-bus-width",	.id = AFIFM6_WRCTRL},
+	{.reg = "xlnx,afi-fs-ss0-bus-width",	.id = AFIFS},
+	{.reg = "xlnx,afi-fs-ss2-bus-width",	.id = AFIFS_SS2},
+	{}
+};
+
+static int zynqmp_afi_config(struct zynqmp_afi *afi_data)
+{
+	struct zynqmp_afi_configreg *cfgptr = afi_cfgreg;
+	struct device_node *np = afi_data->of_node;
+	u32 afi_ss0_val, afi_ss1_val, bus_width;
+	int ret;
+
+	while (cfgptr->reg) {
+		ret = of_property_read_u32(np, cfgptr->reg, &bus_width);
+		if (!ret) {
+			if (cfgptr->id == AFIFS_SS2) {
+				if (bus_width == 32)
+					ret = zynqmp_pm_afi(AFIFS_SS2,
+							    AFIFS_SS_BUS_WIDTH_32_CONFIG_VAL);
+				else if (bus_width == 64)
+					ret = zynqmp_pm_afi(AFIFS_SS2,
+							    AFIFS_SS0_SS2_BUS_WIDTH_64_CONFIG_VAL);
+				else if (bus_width == 128)
+					ret = zynqmp_pm_afi(AFIFS_SS2,
+							    AFIFS_SS0_SS2_BUS_WIDTH_128_CONFIG_VAL);
+				else
+					return -EINVAL;
+			} else if (cfgptr->id == AFIFS) {
+				if (bus_width == 32)
+					afi_ss0_val = AFIFS_SS_BUS_WIDTH_32_CONFIG_VAL;
+				else if (bus_width == 64)
+					afi_ss0_val = AFIFS_SS0_SS2_BUS_WIDTH_64_CONFIG_VAL;
+				else if (bus_width == 128)
+					afi_ss0_val = AFIFS_SS0_SS2_BUS_WIDTH_128_CONFIG_VAL;
+				else
+					return -EINVAL;
+
+				ret = of_property_read_u32(np, "xlnx,afi-fs-ss1-bus-width",
+							   &bus_width);
+				if (!ret) {
+					if (bus_width == 32)
+						afi_ss1_val = AFIFS_SS_BUS_WIDTH_32_CONFIG_VAL;
+					else if (bus_width == 64)
+						afi_ss1_val = AFIFS_SS1_BUS_WIDTH_64_CONFIG_VAL;
+					else if (bus_width == 128)
+						afi_ss1_val = AFIFS_SS1_BUS_WIDTH_128_CONFIG_VAL;
+					else
+						return -EINVAL;
+
+					ret = zynqmp_pm_afi(AFIFS, afi_ss1_val | afi_ss0_val);
+				}
+			} else {
+				if (bus_width == 32)
+					ret = zynqmp_pm_afi(cfgptr->id,
+							    AFIFM_BUS_WIDTH_32_CONFIG_VAL);
+				else if (bus_width == 64)
+					ret = zynqmp_pm_afi(cfgptr->id,
+							    AFIFM_BUS_WIDTH_64_CONFIG_VAL);
+				else if (bus_width == 128)
+					ret = zynqmp_pm_afi(cfgptr->id,
+							    AFIFM_BUS_WIDTH_128_CONFIG_VAL);
+				else
+					return -EINVAL;
+			}
+		}
+		cfgptr++;
+	}
+
+	return 0;
+}
+
+static int zynqmp_afi_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+	struct device_node *overlay = bridge->info->overlay;
+	struct zynqmp_afi *priv = bridge->priv;
+	int ret = 0;
+
+	if (enable) {
+		reset_control_reset(priv->resets);
+		return 0;
+	}
+
+	of_node_get(overlay);
+	priv->of_node =
+	of_find_node_with_property(overlay,
+				   "xlnx,afi-fm0-rd-bus-width");
+	if (priv->of_node)
+		ret = zynqmp_afi_config(priv);
+	of_node_put(priv->of_node);
+
+	return ret;
+}
+
+static const struct fpga_bridge_ops zynqmp_afi_br_ops = {
+	.enable_set = zynqmp_afi_enable_set,
+};
+
+static const struct of_device_id zynqmp_afi_of_match[] = {
+	{ .compatible =  "xlnx,zynqmp-afi-fpga" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, zynqmp_afi_of_match);
+
+static int zynqmp_afi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct zynqmp_afi *priv;
+	struct fpga_bridge *br;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+
+	priv->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev);
+	if (IS_ERR(priv->resets))
+		return PTR_ERR(priv->resets);
+
+	br = fpga_bridge_register(dev, "Xilinx ZynqMP AFI",
+				  &zynqmp_afi_br_ops, priv);
+	if (IS_ERR(br)) {
+		dev_err(dev, "unable to register Xilinx ZynqMP AFI");
+		return PTR_ERR(br);
+	}
+
+	platform_set_drvdata(pdev, br);
+
+	return 0;
+}
+
+static int zynqmp_afi_remove(struct platform_device *pdev)
+{
+	struct fpga_bridge *bridge = platform_get_drvdata(pdev);
+
+	fpga_bridge_unregister(bridge);
+
+	return 0;
+}
+
+static struct platform_driver zynqmp_afi_driver = {
+	.probe = zynqmp_afi_probe,
+	.remove = zynqmp_afi_remove,
+	.driver = {
+		.name = "zynqmp_afi",
+		.of_match_table = of_match_ptr(zynqmp_afi_of_match),
+	},
+};
+
+module_platform_driver(zynqmp_afi_driver);
+
+MODULE_DESCRIPTION("ZYNQMP FPGA AXI FIFO interface configuration module");
+MODULE_AUTHOR("Nava kishore Manne <nava.kishore.manne@xxxxxxx>");
+MODULE_LICENSE("GPL");
-- 
2.25.1




[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux