[RFC 2/3] ARM: socfpga: Add drivers for the SoC-to-FPGA bridges
Steffen Trumtrar
s.trumtrar at pengutronix.de
Thu Dec 11 12:21:41 PST 2014
>From the datasheet:
The HPS-FPGA bridges allow masters in the FPGA fabric to
communicate with slaves in the HPS logic and vice versa.
For example, you can instantiate additional memories or
peripherals in the FPGA fabric, and master interfaces
belonging to components in the HPS logic can access them.
You can also instantiate components such as a NiosII
processor in the FPGA fabric and their master interfaces
can access memories or peripherals in the HPS logic
This patch adds support for the three bridges on SoCFPGA.
Signed-off-by: Steffen Trumtrar <s.trumtrar at pengutronix.de>
---
drivers/soc/socfpga/Kconfig | 43 ++++++
drivers/soc/socfpga/Makefile | 4 +
drivers/soc/socfpga/bridges-common.c | 84 +++++++++++
drivers/soc/socfpga/bridges-common.h | 28 ++++
drivers/soc/socfpga/fpga2hps-bridge.c | 97 ++++++++++++
drivers/soc/socfpga/hps2fpga-bridge.c | 258 ++++++++++++++++++++++++++++++++
drivers/soc/socfpga/lwhps2fpga-bridge.c | 141 +++++++++++++++++
drivers/soc/socfpga/lwhps2fpga.h | 31 ++++
include/soc/socfpga/bridge.h | 65 ++++++++
9 files changed, 751 insertions(+)
create mode 100644 drivers/soc/socfpga/Kconfig
create mode 100644 drivers/soc/socfpga/Makefile
create mode 100644 drivers/soc/socfpga/bridges-common.c
create mode 100644 drivers/soc/socfpga/bridges-common.h
create mode 100644 drivers/soc/socfpga/fpga2hps-bridge.c
create mode 100644 drivers/soc/socfpga/hps2fpga-bridge.c
create mode 100644 drivers/soc/socfpga/lwhps2fpga-bridge.c
create mode 100644 drivers/soc/socfpga/lwhps2fpga.h
create mode 100644 include/soc/socfpga/bridge.h
diff --git a/drivers/soc/socfpga/Kconfig b/drivers/soc/socfpga/Kconfig
new file mode 100644
index 000000000000..963b38f28b91
--- /dev/null
+++ b/drivers/soc/socfpga/Kconfig
@@ -0,0 +1,43 @@
+#
+# SoCFPGA Soc drivers
+#
+
+menuconfig SOCFPGA_BRIDGES
+ bool "SoCFPGA AXI bridges support"
+ depends on ARCH_SOCFPGA
+ select REGMAP_MMIO
+ select SOCFPGA_L3NIC
+ default m
+ help
+ Enable support for the SoCFPGA bridges that connect the HPS to the
+ FPGA.
+
+if SOCFPGA_BRIDGES
+
+config SOCFPGA_LWHPS2FPGA
+ tristate "SoCFPGA LWHPS2FPGA bridge support"
+ depends on ARCH_SOCFPGA
+ select REGMAP_MMIO
+ select SOCFPGA_L3NIC
+ default m
+ help
+ Enable support for the SoCFPGA LWHPS2FPGA bridge.
+
+config SOCFPGA_HPS2FPGA
+ tristate "SoCFPGA HPS2FPGA bridge support"
+ depends on ARCH_SOCFPGA
+ select REGMAP_MMIO
+ select SOCFPGA_L3NIC
+ default m
+ help
+ Enable support for the SoCFPGA HPS2FPGA bridge.
+
+config SOCFPGA_FPGA2HPS
+ tristate "SoCFPGA FPGA2HPS bridge support"
+ depends on ARCH_SOCFPGA
+ select REGMAP_MMIO
+ select SOCFPGA_L3NIC
+ default m
+ help
+ Enable support for the SoCFPGA FPGA2HPS bridge.
+endif
diff --git a/drivers/soc/socfpga/Makefile b/drivers/soc/socfpga/Makefile
new file mode 100644
index 000000000000..948749126907
--- /dev/null
+++ b/drivers/soc/socfpga/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SOCFPGA_BRIDGES) += bridges-common.o
+obj-$(CONFIG_SOCFPGA_LWHPS2FPGA) += lwhps2fpga-bridge.o
+obj-$(CONFIG_SOCFPGA_HPS2FPGA) += hps2fpga-bridge.o
+obj-$(CONFIG_SOCFPGA_FPGA2HPS) += fpga2hps-bridge.o
diff --git a/drivers/soc/socfpga/bridges-common.c b/drivers/soc/socfpga/bridges-common.c
new file mode 100644
index 000000000000..2acc77c61df5
--- /dev/null
+++ b/drivers/soc/socfpga/bridges-common.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <soc/socfpga/bridge.h>
+#include <soc/socfpga/gpv.h>
+#include "bridges-common.h"
+
+int socfpga_bridge_reset_probe(struct platform_device *pdev,
+ struct socfpga_bridge *priv,
+ const char *name)
+{
+ priv->rst = devm_reset_control_get(&pdev->dev, name);
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+
+ return reset_control_deassert(priv->rst);
+}
+EXPORT_SYMBOL_GPL(socfpga_bridge_reset_probe);
+
+struct regmap *socfpga_bridge_regmap_by_phandle(struct device_node *np,
+ const char *name)
+{
+ struct socfpga_bridge *bridge;
+ struct platform_device *pdev;
+
+ pdev = socfpga_gpv_device_by_phandle(np, name);
+ if (IS_ERR(pdev))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ bridge = dev_get_drvdata(&pdev->dev);
+ if (IS_ERR(bridge))
+ return ERR_PTR(-EINVAL);
+
+ return bridge->regmap;
+}
+EXPORT_SYMBOL_GPL(socfpga_bridge_regmap_by_phandle);
+
+int socfpga_bridge_probe(struct platform_device *pdev,
+ const struct of_device_id *dt_ids)
+{
+ struct socfpga_bridge *priv;
+ struct resource *res;
+ const struct of_device_id *device;
+ unsigned int bus_width;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ device = of_match_device(dt_ids, &pdev->dev);
+ if (!device)
+ return -ENODEV;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpv");
+ if (!res)
+ return -EADDRNOTAVAIL;
+
+ priv->gpv_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->gpv_base))
+ return PTR_ERR(priv->gpv_base);
+
+ priv->max_register = res->end - res->start - 3;
+
+ if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width))
+ priv->width = bus_width;
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(socfpga_bridge_probe);
diff --git a/drivers/soc/socfpga/bridges-common.h b/drivers/soc/socfpga/bridges-common.h
new file mode 100644
index 000000000000..513efbb29a0e
--- /dev/null
+++ b/drivers/soc/socfpga/bridges-common.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOC_SOCFPGA_BRIDGES_COMMON_H__
+#define __SOC_SOCFPGA_BRIDGES_COMMON_H__
+
+struct socfpga_bridge;
+struct platform_device;
+struct platform_driver;
+struct device_node;
+
+extern int socfpga_bridge_reset_probe(struct platform_device *pdev,
+ struct socfpga_bridge *priv,
+ const char *name);
+
+extern struct regmap *socfpga_bridge_regmap_by_phandle(struct device_node *np,
+ const char *name);
+
+extern int socfpga_bridge_probe(struct platform_device *pdev,
+ const struct of_device_id *dt_ids);
+
+#endif
diff --git a/drivers/soc/socfpga/fpga2hps-bridge.c b/drivers/soc/socfpga/fpga2hps-bridge.c
new file mode 100644
index 000000000000..8bf3a42a03ee
--- /dev/null
+++ b/drivers/soc/socfpga/fpga2hps-bridge.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <soc/socfpga/bridge.h>
+#include <soc/socfpga/gpv.h>
+#include <soc/socfpga/l3regs.h>
+#include "bridges-common.h"
+
+MODULE_PARM_DESC(fpga2hps_wr_trans, "Enable multiple outstanding write transactions for FPGA2HPS (default, 'y')");
+static bool fpga2hps_wr_trans = true;
+module_param(fpga2hps_wr_trans, bool, 0644);
+
+MODULE_PARM_DESC(fpga2hps_rd_trans, "Enable multiple outstanding read transactions for FPGA2HPS (default, 'y')");
+static bool fpga2hps_rd_trans = true;
+module_param(fpga2hps_rd_trans, bool, 0644);
+
+static const struct of_device_id socfpga_fpga2hps_dt_ids[] = {
+ { .compatible = "altr,fpga2hps-axi-bridge", },
+ { /* sentinel */ },
+};
+
+static int socfpga_fpga2hps_probe(struct platform_device *pdev)
+{
+ struct socfpga_bridge *priv;
+ int ret;
+
+ ret = socfpga_bridge_probe(pdev, socfpga_fpga2hps_dt_ids);
+ if (ret)
+ return ret;
+
+ priv = platform_get_drvdata(pdev);
+ ret = socfpga_bridge_reset_probe(pdev, priv, "fpga2hps");
+ if (ret)
+ return ret;
+
+ priv->gpv_clk = devm_clk_get(&pdev->dev, "gpv_clk");
+ if (IS_ERR(priv->gpv_clk))
+ return PTR_ERR(priv->gpv_clk);
+
+ clk_prepare_enable(priv->gpv_clk);
+
+ priv->clk = devm_clk_get(&pdev->dev, "data_clk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ clk_prepare_enable(priv->clk);
+
+ if (priv->width == 32 || priv->width == 128) {
+ priv->gpv_master = socfpga_bridge_regmap_by_phandle(
+ pdev->dev.of_node,
+ "altr,bridge-gpv");
+ if (IS_ERR(priv->gpv_master))
+ return -EPROBE_DEFER;
+
+ dev_info(&pdev->dev, "HPS2FPGA GPV registered\n");
+ }
+
+ dev_info(&pdev->dev, "FPGA2HPS registered\n");
+
+ return 0;
+}
+
+static int socfpga_fpga2hps_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver socfpga_fpga2hps_driver = {
+ .probe = socfpga_fpga2hps_probe,
+ .remove = socfpga_fpga2hps_remove,
+ .driver = {
+ .name = "socfpga-fpga2hps-bridge",
+ .owner = THIS_MODULE,
+ .of_match_table = socfpga_fpga2hps_dt_ids,
+ },
+};
+module_platform_driver(socfpga_fpga2hps_driver);
+
+MODULE_AUTHOR("Steffen Trumtrar <s.trumtrar at pengutronix.de");
+MODULE_DESCRIPTION("Socfpga AXI-FPGA2HPS-Bridge Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/socfpga/hps2fpga-bridge.c b/drivers/soc/socfpga/hps2fpga-bridge.c
new file mode 100644
index 000000000000..1f0f58134594
--- /dev/null
+++ b/drivers/soc/socfpga/hps2fpga-bridge.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <soc/socfpga/bridge.h>
+#include <soc/socfpga/gpv.h>
+#include <soc/socfpga/l3regs.h>
+#include "bridges-common.h"
+#include "lwhps2fpga.h"
+
+MODULE_PARM_DESC(wr_trans, "Enable multiple outstanding write transactions for HPS2FPGA AXI bus (default, 'y')");
+static bool wr_trans = true;
+module_param(wr_trans, bool, 0644);
+
+MODULE_PARM_DESC(rd_trans, "Enable multiple outstanding read transactions for HPS2FPGA AXI bus (default, 'y'')");
+static bool rd_trans = true;
+module_param(rd_trans, bool, 0644);
+
+MODULE_PARM_DESC(force_incr, "Enable forcing of INCR for HPS2FPGA AHB (default, 'n')");
+static bool force_incr;
+module_param(force_incr, bool, 0644);
+
+MODULE_PARM_DESC(decerr_en, "Enable DECERR responses for HPS2FPGA AHB (default, 'n'')");
+static bool decerr_en;
+module_param(decerr_en, bool, 0644);
+
+MODULE_PARM_DESC(bypass_merge, "Enable upsizing/downsizing of transactions for HPS2FPGA AXI bus (default, 'y')");
+static bool bypass_merge = true;
+module_param(bypass_merge, bool, 0644);
+
+#define HPS2FPGA_PERIPH_ID_4 0x1fd0
+#define HPS2FPGA_PERIPH_ID_0 0x1fe0
+#define HPS2FPGA_COMP_ID_3 0x1ffc
+#define HPS2FPGA_32BIT_FN_MOD2 0x2024
+#define HPS2FPGA_32BIT_FN_MOD 0x2108
+#define HPS2FPGA_128BIT_FN_MOD2 0x4024
+#define HPS2FPGA_128BIT_FN_MOD 0x4108
+
+static const struct regmap_range hps_read_regs_range[] = {
+ regmap_reg_range(HPS2FPGA_PERIPH_ID_4, HPS2FPGA_PERIPH_ID_4),
+ regmap_reg_range(HPS2FPGA_PERIPH_ID_0, HPS2FPGA_COMP_ID_3),
+ regmap_reg_range(HPS2FPGA_32BIT_FN_MOD2, HPS2FPGA_32BIT_FN_MOD2),
+ regmap_reg_range(HPS2FPGA_32BIT_FN_MOD, HPS2FPGA_32BIT_FN_MOD),
+ regmap_reg_range(HPS2FPGA_128BIT_FN_MOD2, HPS2FPGA_128BIT_FN_MOD2),
+ regmap_reg_range(HPS2FPGA_128BIT_FN_MOD, HPS2FPGA_128BIT_FN_MOD),
+};
+
+static const struct regmap_range hps_write_regs_range[] = {
+ regmap_reg_range(HPS2FPGA_32BIT_FN_MOD2, HPS2FPGA_32BIT_FN_MOD2),
+ regmap_reg_range(HPS2FPGA_32BIT_FN_MOD, HPS2FPGA_32BIT_FN_MOD),
+ regmap_reg_range(HPS2FPGA_128BIT_FN_MOD2, HPS2FPGA_128BIT_FN_MOD2),
+ regmap_reg_range(HPS2FPGA_128BIT_FN_MOD, HPS2FPGA_128BIT_FN_MOD),
+};
+
+static const struct regmap_access_table hps_read_regs = {
+ .yes_ranges = hps_read_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(hps_read_regs_range),
+};
+
+static const struct regmap_access_table hps_write_regs = {
+ .yes_ranges = hps_write_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(hps_write_regs_range),
+};
+
+static struct regmap_config hps_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .rd_table = &hps_read_regs,
+ .wr_table = &hps_write_regs,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int socfpga_hps2fpga_gpv_probe(struct platform_device *pdev)
+{
+ struct socfpga_bridge *priv;
+ int ret;
+ int val;
+ int mask = 0;
+ int reg;
+
+ priv = platform_get_drvdata(pdev);
+
+ hps_regmap_config.max_register = priv->max_register;
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->gpv_base,
+ &hps_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->gpv_master = socfpga_bridge_regmap_by_phandle(
+ pdev->dev.of_node,
+ "altr,bridge-gpv");
+ if (IS_ERR(priv->gpv_master))
+ return -EPROBE_DEFER;
+
+ dev_info(&pdev->dev, "HPS2FPGA GPV registered\n");
+
+ mask |= GPV_FN_MOD_BM_ISS_WR | GPV_FN_MOD_BM_ISS_RD;
+ reg = LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS;
+
+ val = 0;
+ if (!wr_trans)
+ val |= GPV_FN_MOD_BM_ISS_WR;
+
+ if (!rd_trans)
+ val |= GPV_FN_MOD_BM_ISS_RD;
+
+ ret = regmap_update_bits(priv->gpv_master, reg, mask, val);
+ if (ret) {
+ dev_err(&pdev->dev, "GPV configuration failure reg=0x%08x (%d)\n", reg, ret);
+ return ret;
+ }
+
+ reg = priv->width == 32 ? HPS2FPGA_32BIT_FN_MOD : HPS2FPGA_128BIT_FN_MOD;
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(&pdev->dev, "Configuration failure reg=0x%08x (%d)\n", reg, ret);
+ return ret;
+ }
+
+ val = 0;
+ mask = 0;
+ mask |= GPV_AHB_CNTL_FORCE_INCR | GPV_AHB_CNTL_DECERR_EN;
+ reg = LWHPS2FPGA_HPS2FPGA_AHB_CNTL;
+
+ if (force_incr)
+ val |= GPV_AHB_CNTL_FORCE_INCR;
+
+ if (decerr_en)
+ val |= GPV_AHB_CNTL_DECERR_EN;
+
+ ret = regmap_update_bits(priv->gpv_master, reg, mask, val);
+ if (ret) {
+ dev_err(&pdev->dev, "GPV configuration failure reg=0x%08x (%d)\n", reg, ret);
+ return ret;
+ }
+
+ val = 0;
+ mask = 0;
+ mask |= GPV_FN_MOD_BYPASS_MERGE;
+ reg = priv->width == 32 ? HPS2FPGA_32BIT_FN_MOD2 : HPS2FPGA_128BIT_FN_MOD2;
+
+ if (bypass_merge)
+ val |= GPV_FN_MOD_BYPASS_MERGE;
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(&pdev->dev, "Configuration failure reg=0x%08x (%d)\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id socfpga_hps2fpga_dt_ids[] = {
+ { .compatible = "altr,hps2fpga-axi-bridge", },
+ { /* sentinel */ },
+};
+
+static int socfpga_hps2fpga_probe(struct platform_device *pdev)
+{
+ struct socfpga_bridge *priv;
+ int ret;
+
+ ret = socfpga_bridge_probe(pdev, socfpga_hps2fpga_dt_ids);
+ if (ret)
+ return ret;
+
+ priv = platform_get_drvdata(pdev);
+
+ ret = socfpga_bridge_reset_probe(pdev, priv, "hps2fpga");
+ if (ret)
+ return ret;
+
+ priv->gpv_clk = devm_clk_get(&pdev->dev, "gpv_clk");
+ if (IS_ERR(priv->gpv_clk))
+ return PTR_ERR(priv->gpv_clk);
+
+ clk_prepare_enable(priv->gpv_clk);
+
+ priv->clk = devm_clk_get(&pdev->dev, "data_clk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ clk_prepare_enable(priv->clk);
+
+ if (priv->width == 32 || priv->width == 128) {
+ ret = socfpga_hps2fpga_gpv_probe(pdev);
+ if (ret)
+ return ret;
+ }
+
+ priv->l3regs = socfpga_l3nic_regmap_by_phandle(pdev->dev.of_node,
+ "altr,l3-gpv");
+ if (IS_ERR(priv->l3regs)) {
+ dev_warn(&pdev->dev, "No GPV node found. Can not change bridge visibility\n");
+ return PTR_ERR(-EPROBE_DEFER);
+ } else {
+ ret = regmap_update_bits(priv->l3regs, L3NIC_REMAP,
+ L3NIC_HPS2FPGA_VISIBILITY,
+ L3NIC_HPS2FPGA_VISIBILITY);
+ if (ret) {
+ dev_err(&pdev->dev, "Bridge visibility not set. Access not possible\n");
+ return ret;
+ }
+ }
+
+ dev_info(&pdev->dev, "HPS2FPGA registered\n");
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}
+
+static int socfpga_hps2fpga_remove(struct platform_device *pdev)
+{
+ struct socfpga_bridge *priv;
+
+ priv = platform_get_drvdata(pdev);
+
+ socfpga_bridge_reset_assert(priv);
+
+ clk_disable_unprepare(priv->gpv_clk);
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static struct platform_driver socfpga_hps2fpga_driver = {
+ .probe = socfpga_hps2fpga_probe,
+ .remove = socfpga_hps2fpga_remove,
+ .driver = {
+ .name = "socfpga-hps2fpga-bridge",
+ .owner = THIS_MODULE,
+ .of_match_table = socfpga_hps2fpga_dt_ids,
+ },
+};
+module_platform_driver(socfpga_hps2fpga_driver);
+
+MODULE_AUTHOR("Steffen Trumtrar <s.trumtrar at pengutronix.de");
+MODULE_DESCRIPTION("Socfpga AXI-HPS2FPGA-Bridge Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/socfpga/lwhps2fpga-bridge.c b/drivers/soc/socfpga/lwhps2fpga-bridge.c
new file mode 100644
index 000000000000..18282d159bf3
--- /dev/null
+++ b/drivers/soc/socfpga/lwhps2fpga-bridge.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <soc/socfpga/bridge.h>
+#include <soc/socfpga/l3regs.h>
+#include "bridges-common.h"
+#include "lwhps2fpga.h"
+
+static const struct regmap_range lwhps_read_regs_range[] = {
+ regmap_reg_range(LWHPS2FPGA_PERIPH_ID_4, LWHPS2FPGA_PERIPH_ID_4),
+ regmap_reg_range(LWHPS2FPGA_PERIPH_ID_0, LWHPS2FPGA_COMP_ID_3),
+ regmap_reg_range(LWHPS2FPGA_FPGA2HPS_FN_MOD_BM_ISS, LWHPS2FPGA_FPGA2HPS_FN_MOD_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_FPGA2HPS_AHB_CNTL, LWHPS2FPGA_FPGA2HPS_AHB_CNTL),
+ regmap_reg_range(LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS, LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_HPS2FPGA_AHB_CNTL, LWHPS2FPGA_HPS2FPGA_AHB_CNTL),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_FN_MODE_BM_ISS, LWHPS2FPGA_32BIT_MASTER_FN_MODE_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_WR_TIDEMARK, LWHPS2FPGA_32BIT_MASTER_WR_TIDEMARK),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_FN_MOD, LWHPS2FPGA_32BIT_MASTER_FN_MOD),
+ regmap_reg_range(LWHPS2FPGA_L3_SLAVE_FN_MODE, LWHPS2FPGA_L3_SLAVE_FN_MODE),
+};
+
+static const struct regmap_range lwhps_write_regs_range[] = {
+ regmap_reg_range(LWHPS2FPGA_FPGA2HPS_FN_MOD_BM_ISS, LWHPS2FPGA_FPGA2HPS_FN_MOD_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_FPGA2HPS_AHB_CNTL, LWHPS2FPGA_FPGA2HPS_AHB_CNTL),
+ regmap_reg_range(LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS, LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_HPS2FPGA_AHB_CNTL, LWHPS2FPGA_HPS2FPGA_AHB_CNTL),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_FN_MODE_BM_ISS, LWHPS2FPGA_32BIT_MASTER_FN_MODE_BM_ISS),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_WR_TIDEMARK, LWHPS2FPGA_32BIT_MASTER_WR_TIDEMARK),
+ regmap_reg_range(LWHPS2FPGA_32BIT_MASTER_FN_MOD, LWHPS2FPGA_32BIT_MASTER_FN_MOD),
+ regmap_reg_range(LWHPS2FPGA_L3_SLAVE_FN_MODE, LWHPS2FPGA_L3_SLAVE_FN_MODE),
+};
+
+static const struct regmap_access_table lwhps_read_regs = {
+ .yes_ranges = lwhps_read_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(lwhps_read_regs_range),
+};
+
+static const struct regmap_access_table lwhps_write_regs = {
+ .yes_ranges = lwhps_write_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(lwhps_write_regs_range),
+};
+
+static struct regmap_config lwhps_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .rd_table = &lwhps_read_regs,
+ .wr_table = &lwhps_write_regs,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct of_device_id socfpga_lwhps2fpga_dt_ids[] = {
+ { .compatible = "altr,lwhps2fpga-axi-bridge", },
+ { /* sentinel */ },
+};
+
+static int socfpga_lwhps2fpga_probe(struct platform_device *pdev)
+{
+ struct socfpga_bridge *priv;
+ int ret;
+
+ ret = socfpga_bridge_probe(pdev, socfpga_lwhps2fpga_dt_ids);
+ if (ret)
+ return ret;
+
+ priv = platform_get_drvdata(pdev);
+
+ ret = socfpga_bridge_reset_probe(pdev, priv, "lwhps2fpga");
+ if (ret)
+ return ret;
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ clk_prepare_enable(priv->clk);
+
+ lwhps_regmap_config.max_register = priv->max_register;
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->gpv_base,
+ &lwhps_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->l3regs = socfpga_l3nic_regmap_by_phandle(pdev->dev.of_node,
+ "altr,l3-gpv");
+ if (IS_ERR(priv->l3regs)) {
+ dev_warn(&pdev->dev, "No GPV node found. Can not change bridge visibility\n");
+ return ERR_PTR(-EPROBE_DEFER);
+ } else {
+ ret = regmap_update_bits(priv->l3regs, L3NIC_REMAP,
+ L3NIC_LWHPS2FPGA_VISIBILITY,
+ L3NIC_LWHPS2FPGA_VISIBILITY);
+ if (ret) {
+ dev_err(&pdev->dev, "Bridge visibility not set. Access not possible\n");
+ return ret;
+ }
+ }
+
+ dev_info(&pdev->dev, "LWHPS2FPGA registered\n");
+
+ return 0;
+}
+
+static int socfpga_lwhps2fpga_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver socfpga_lwhps2fpga_driver = {
+ .probe = socfpga_lwhps2fpga_probe,
+ .remove = socfpga_lwhps2fpga_remove,
+ .driver = {
+ .name = "socfpga-lwhps2fgpa-bridge",
+ .owner = THIS_MODULE,
+ .of_match_table = socfpga_lwhps2fpga_dt_ids,
+ },
+};
+module_platform_driver(socfpga_lwhps2fpga_driver);
+
+MODULE_AUTHOR("Steffen Trumtrar <s.trumtrar at pengutronix.de");
+MODULE_DESCRIPTION("Socfpga AXI-LWHPS2FPGA-Bridge Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/socfpga/lwhps2fpga.h b/drivers/soc/socfpga/lwhps2fpga.h
new file mode 100644
index 000000000000..d1882ba2ed81
--- /dev/null
+++ b/drivers/soc/socfpga/lwhps2fpga.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOC_SOCFPGA_LWHPS2FPGA_H__
+#define __SOC_SOCFPGA_LWHPS2FPGA_H__
+
+#define LWHPS2FPGA_PERIPH_ID_4 0x1FD0
+#define LWHPS2FPGA_PERIPH_ID_0 0x1FE0
+#define LWHPS2FPGA_PERIPH_ID_1 0x1FE4
+#define LWHPS2FPGA_PERIPH_ID_2 0x1FE8
+#define LWHPS2FPGA_PERIPH_ID_3 0x1FEC
+#define LWHPS2FPGA_COMP_ID_0 0x1FF0
+#define LWHPS2FPGA_COMP_ID_1 0x1FF4
+#define LWHPS2FPGA_COMP_ID_2 0x1FF8
+#define LWHPS2FPGA_COMP_ID_3 0x1FFC
+#define LWHPS2FPGA_FPGA2HPS_FN_MOD_BM_ISS 0x2008
+#define LWHPS2FPGA_FPGA2HPS_AHB_CNTL 0x2044
+#define LWHPS2FPGA_HPS2FPGA_FN_MOD_BM_ISS 0x3008
+#define LWHPS2FPGA_HPS2FPGA_AHB_CNTL 0x3044
+#define LWHPS2FPGA_32BIT_MASTER_FN_MODE_BM_ISS 0x5008
+#define LWHPS2FPGA_32BIT_MASTER_WR_TIDEMARK 0x5048
+#define LWHPS2FPGA_32BIT_MASTER_FN_MOD 0x5108
+#define LWHPS2FPGA_L3_SLAVE_FN_MODE 0x45108
+
+#endif
diff --git a/include/soc/socfpga/bridge.h b/include/soc/socfpga/bridge.h
new file mode 100644
index 000000000000..a2159da126b0
--- /dev/null
+++ b/include/soc/socfpga/bridge.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Steffen Trumtrar <s.trumtrar at pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOC_SOCFPGA_BRIDGE_H__
+#define __SOC_SOCFPGA_BRIDGE_H__
+
+#ifdef CONFIG_ARCH_SOCFPGA
+
+#include <linux/reset.h>
+
+struct socfpga_bridge {
+ void __iomem *gpv_base;
+ struct reset_control *rst;
+ struct regmap *regmap;
+ struct regmap *l3regs;
+ struct regmap *gpv_master;
+ unsigned int max_register;
+ struct clk *clk;
+ struct clk *gpv_clk;
+ unsigned int width;
+};
+
+struct socfpga_bridge *socfpga_bridge_get(struct device_node *np,
+ const char *name);
+
+static inline int socfpga_bridge_reset_assert(struct socfpga_bridge *priv)
+{
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+
+ return reset_control_assert(priv->rst);
+}
+
+static inline int socfpga_bridge_reset_deassert(struct socfpga_bridge *priv)
+{
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+
+ return reset_control_assert(priv->rst);
+}
+#else
+struct socfpga_bridge *socfpga_bridge_get(struct device_node *np,
+ const char *name)
+{
+ return -ENOSYS;
+}
+
+static inline int socfpga_bridge_reset_assert(struct socfpga_bridge *priv)
+{
+ return -ENOSYS;
+}
+
+static inline int socfpga_bridge_reset_deassert(struct socfpga_bridge *priv)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif
--
2.1.3
More information about the linux-arm-kernel
mailing list