Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: socfpga: fpga bridge driver support

Supports Altera SOCFPGA bridges:
* fpga2sdram
* fpga2hps
* hps2fpga
* lwhps2fpga

Allows enabling/disabling the bridges through the FPGA
Bridge Framework API functions.

The fpga2sdram driver only supports enabling and disabling
of the ports that been configured early on. This is due to
a hardware limitation where the read, write, and command
ports on the fpga2sdram bridge can only be reconfigured
while there are no transactions to the sdram, i.e. when
running out of OCRAM before the kernel boots.

Device tree property 'init-val' configures the driver to
enable or disable the bridge during probe. If the property
does not exist, the driver will leave the bridge in its
current state.

Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Matthew Gerlach <mgerlach@altera.com>
Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Tull and committed by
Greg Kroah-Hartman
e5f8efa5 0fa20cdf

+410
+7
drivers/fpga/Kconfig
··· 40 40 Say Y here if you want to support bridges connected between host 41 41 processors and FPGAs or between FPGAs. 42 42 43 + config SOCFPGA_FPGA_BRIDGE 44 + tristate "Altera SoCFPGA FPGA Bridges" 45 + depends on ARCH_SOCFPGA && FPGA_BRIDGE 46 + help 47 + Say Y to enable drivers for FPGA bridges for Altera SOCFPGA 48 + devices. 49 + 43 50 endif # FPGA 44 51 45 52 endmenu
+1
drivers/fpga/Makefile
··· 11 11 12 12 # FPGA Bridge Drivers 13 13 obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o 14 + obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o 14 15 15 16 # High Level Interfaces 16 17 obj-$(CONFIG_FPGA_REGION) += fpga-region.o
+180
drivers/fpga/altera-fpga2sdram.c
··· 1 + /* 2 + * FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices 3 + * 4 + * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + * 15 + * You should have received a copy of the GNU General Public License along with 16 + * this program. If not, see <http://www.gnu.org/licenses/>. 17 + */ 18 + 19 + /* 20 + * This driver manages a bridge between an FPGA and the SDRAM used by the ARM 21 + * host processor system (HPS). 22 + * 23 + * The bridge contains 4 read ports, 4 write ports, and 6 command ports. 24 + * Reconfiguring these ports requires that no SDRAM transactions occur during 25 + * reconfiguration. The code reconfiguring the ports cannot run out of SDRAM 26 + * nor can the FPGA access the SDRAM during reconfiguration. This driver does 27 + * not support reconfiguring the ports. The ports are configured by code 28 + * running out of on chip ram before Linux is started and the configuration 29 + * is passed in a handoff register in the system manager. 30 + * 31 + * This driver supports enabling and disabling of the configured ports, which 32 + * allows for safe reprogramming of the FPGA, assuming that the new FPGA image 33 + * uses the same port configuration. Bridges must be disabled before 34 + * reprogramming the FPGA and re-enabled after the FPGA has been programmed. 35 + */ 36 + 37 + #include <linux/fpga/fpga-bridge.h> 38 + #include <linux/kernel.h> 39 + #include <linux/mfd/syscon.h> 40 + #include <linux/module.h> 41 + #include <linux/of_platform.h> 42 + #include <linux/regmap.h> 43 + 44 + #define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80 45 + #define ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK 0x00003fff 46 + #define ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT 0 47 + #define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4 48 + #define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8 49 + 50 + /* 51 + * From the Cyclone V HPS Memory Map document: 52 + * These registers are used to store handoff information between the 53 + * preloader and the OS. These 8 registers can be used to store any 54 + * information. The contents of these registers have no impact on 55 + * the state of the HPS hardware. 56 + */ 57 + #define SYSMGR_ISWGRP_HANDOFF3 (0x8C) 58 + 59 + #define F2S_BRIDGE_NAME "fpga2sdram" 60 + 61 + struct alt_fpga2sdram_data { 62 + struct device *dev; 63 + struct regmap *sdrctl; 64 + int mask; 65 + }; 66 + 67 + static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge) 68 + { 69 + struct alt_fpga2sdram_data *priv = bridge->priv; 70 + int value; 71 + 72 + regmap_read(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, &value); 73 + 74 + return (value & priv->mask) == priv->mask; 75 + } 76 + 77 + static inline int _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv, 78 + bool enable) 79 + { 80 + return regmap_update_bits(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, 81 + priv->mask, enable ? priv->mask : 0); 82 + } 83 + 84 + static int alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) 85 + { 86 + return _alt_fpga2sdram_enable_set(bridge->priv, enable); 87 + } 88 + 89 + struct prop_map { 90 + char *prop_name; 91 + u32 *prop_value; 92 + u32 prop_max; 93 + }; 94 + 95 + static const struct fpga_bridge_ops altera_fpga2sdram_br_ops = { 96 + .enable_set = alt_fpga2sdram_enable_set, 97 + .enable_show = alt_fpga2sdram_enable_show, 98 + }; 99 + 100 + static const struct of_device_id altera_fpga_of_match[] = { 101 + { .compatible = "altr,socfpga-fpga2sdram-bridge" }, 102 + {}, 103 + }; 104 + 105 + static int alt_fpga_bridge_probe(struct platform_device *pdev) 106 + { 107 + struct device *dev = &pdev->dev; 108 + struct alt_fpga2sdram_data *priv; 109 + u32 enable; 110 + struct regmap *sysmgr; 111 + int ret = 0; 112 + 113 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 114 + if (!priv) 115 + return -ENOMEM; 116 + 117 + priv->dev = dev; 118 + 119 + priv->sdrctl = syscon_regmap_lookup_by_compatible("altr,sdr-ctl"); 120 + if (IS_ERR(priv->sdrctl)) { 121 + dev_err(dev, "regmap for altr,sdr-ctl lookup failed.\n"); 122 + return PTR_ERR(priv->sdrctl); 123 + } 124 + 125 + sysmgr = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); 126 + if (IS_ERR(priv->sdrctl)) { 127 + dev_err(dev, "regmap for altr,sys-mgr lookup failed.\n"); 128 + return PTR_ERR(sysmgr); 129 + } 130 + 131 + /* Get f2s bridge configuration saved in handoff register */ 132 + regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); 133 + 134 + ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME, 135 + &altera_fpga2sdram_br_ops, priv); 136 + if (ret) 137 + return ret; 138 + 139 + dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); 140 + 141 + if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { 142 + if (enable > 1) { 143 + dev_warn(dev, "invalid bridge-enable %u > 1\n", enable); 144 + } else { 145 + dev_info(dev, "%s bridge\n", 146 + (enable ? "enabling" : "disabling")); 147 + ret = _alt_fpga2sdram_enable_set(priv, enable); 148 + if (ret) { 149 + fpga_bridge_unregister(&pdev->dev); 150 + return ret; 151 + } 152 + } 153 + } 154 + 155 + return ret; 156 + } 157 + 158 + static int alt_fpga_bridge_remove(struct platform_device *pdev) 159 + { 160 + fpga_bridge_unregister(&pdev->dev); 161 + 162 + return 0; 163 + } 164 + 165 + MODULE_DEVICE_TABLE(of, altera_fpga_of_match); 166 + 167 + static struct platform_driver altera_fpga_driver = { 168 + .probe = alt_fpga_bridge_probe, 169 + .remove = alt_fpga_bridge_remove, 170 + .driver = { 171 + .name = "altera_fpga2sdram_bridge", 172 + .of_match_table = of_match_ptr(altera_fpga_of_match), 173 + }, 174 + }; 175 + 176 + module_platform_driver(altera_fpga_driver); 177 + 178 + MODULE_DESCRIPTION("Altera SoCFPGA FPGA to SDRAM Bridge"); 179 + MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); 180 + MODULE_LICENSE("GPL v2");
+222
drivers/fpga/altera-hps2fpga.c
··· 1 + /* 2 + * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices 3 + * 4 + * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. 5 + * 6 + * Includes this patch from the mailing list: 7 + * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters 8 + * Signed-off-by: Anatolij Gustschin <agust@denx.de> 9 + * 10 + * This program is free software; you can redistribute it and/or modify it 11 + * under the terms and conditions of the GNU General Public License, 12 + * version 2, as published by the Free Software Foundation. 13 + * 14 + * This program is distributed in the hope it will be useful, but WITHOUT 15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 + * more details. 18 + * 19 + * You should have received a copy of the GNU General Public License along with 20 + * this program. If not, see <http://www.gnu.org/licenses/>. 21 + */ 22 + 23 + /* 24 + * This driver manages bridges on a Altera SOCFPGA between the ARM host 25 + * processor system (HPS) and the embedded FPGA. 26 + * 27 + * This driver supports enabling and disabling of the configured ports, which 28 + * allows for safe reprogramming of the FPGA, assuming that the new FPGA image 29 + * uses the same port configuration. Bridges must be disabled before 30 + * reprogramming the FPGA and re-enabled after the FPGA has been programmed. 31 + */ 32 + 33 + #include <linux/clk.h> 34 + #include <linux/fpga/fpga-bridge.h> 35 + #include <linux/kernel.h> 36 + #include <linux/mfd/syscon.h> 37 + #include <linux/module.h> 38 + #include <linux/of_platform.h> 39 + #include <linux/regmap.h> 40 + #include <linux/reset.h> 41 + #include <linux/spinlock.h> 42 + 43 + #define ALT_L3_REMAP_OFST 0x0 44 + #define ALT_L3_REMAP_MPUZERO_MSK 0x00000001 45 + #define ALT_L3_REMAP_H2F_MSK 0x00000008 46 + #define ALT_L3_REMAP_LWH2F_MSK 0x00000010 47 + 48 + #define HPS2FPGA_BRIDGE_NAME "hps2fpga" 49 + #define LWHPS2FPGA_BRIDGE_NAME "lwhps2fpga" 50 + #define FPGA2HPS_BRIDGE_NAME "fpga2hps" 51 + 52 + struct altera_hps2fpga_data { 53 + const char *name; 54 + struct reset_control *bridge_reset; 55 + struct regmap *l3reg; 56 + unsigned int remap_mask; 57 + struct clk *clk; 58 + }; 59 + 60 + static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) 61 + { 62 + struct altera_hps2fpga_data *priv = bridge->priv; 63 + 64 + return reset_control_status(priv->bridge_reset); 65 + } 66 + 67 + /* The L3 REMAP register is write only, so keep a cached value. */ 68 + static unsigned int l3_remap_shadow; 69 + static spinlock_t l3_remap_lock; 70 + 71 + static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv, 72 + bool enable) 73 + { 74 + unsigned long flags; 75 + int ret; 76 + 77 + /* bring bridge out of reset */ 78 + if (enable) 79 + ret = reset_control_deassert(priv->bridge_reset); 80 + else 81 + ret = reset_control_assert(priv->bridge_reset); 82 + if (ret) 83 + return ret; 84 + 85 + /* Allow bridge to be visible to L3 masters or not */ 86 + if (priv->remap_mask) { 87 + spin_lock_irqsave(&l3_remap_lock, flags); 88 + l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK; 89 + 90 + if (enable) 91 + l3_remap_shadow |= priv->remap_mask; 92 + else 93 + l3_remap_shadow &= ~priv->remap_mask; 94 + 95 + ret = regmap_write(priv->l3reg, ALT_L3_REMAP_OFST, 96 + l3_remap_shadow); 97 + spin_unlock_irqrestore(&l3_remap_lock, flags); 98 + } 99 + 100 + return ret; 101 + } 102 + 103 + static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) 104 + { 105 + return _alt_hps2fpga_enable_set(bridge->priv, enable); 106 + } 107 + 108 + static const struct fpga_bridge_ops altera_hps2fpga_br_ops = { 109 + .enable_set = alt_hps2fpga_enable_set, 110 + .enable_show = alt_hps2fpga_enable_show, 111 + }; 112 + 113 + static struct altera_hps2fpga_data hps2fpga_data = { 114 + .name = HPS2FPGA_BRIDGE_NAME, 115 + .remap_mask = ALT_L3_REMAP_H2F_MSK, 116 + }; 117 + 118 + static struct altera_hps2fpga_data lwhps2fpga_data = { 119 + .name = LWHPS2FPGA_BRIDGE_NAME, 120 + .remap_mask = ALT_L3_REMAP_LWH2F_MSK, 121 + }; 122 + 123 + static struct altera_hps2fpga_data fpga2hps_data = { 124 + .name = FPGA2HPS_BRIDGE_NAME, 125 + }; 126 + 127 + static const struct of_device_id altera_fpga_of_match[] = { 128 + { .compatible = "altr,socfpga-hps2fpga-bridge", 129 + .data = &hps2fpga_data }, 130 + { .compatible = "altr,socfpga-lwhps2fpga-bridge", 131 + .data = &lwhps2fpga_data }, 132 + { .compatible = "altr,socfpga-fpga2hps-bridge", 133 + .data = &fpga2hps_data }, 134 + {}, 135 + }; 136 + 137 + static int alt_fpga_bridge_probe(struct platform_device *pdev) 138 + { 139 + struct device *dev = &pdev->dev; 140 + struct altera_hps2fpga_data *priv; 141 + const struct of_device_id *of_id; 142 + u32 enable; 143 + int ret; 144 + 145 + of_id = of_match_device(altera_fpga_of_match, dev); 146 + priv = (struct altera_hps2fpga_data *)of_id->data; 147 + 148 + priv->bridge_reset = of_reset_control_get_by_index(dev->of_node, 0); 149 + if (IS_ERR(priv->bridge_reset)) { 150 + dev_err(dev, "Could not get %s reset control\n", priv->name); 151 + return PTR_ERR(priv->bridge_reset); 152 + } 153 + 154 + if (priv->remap_mask) { 155 + priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs"); 156 + if (IS_ERR(priv->l3reg)) { 157 + dev_err(dev, "regmap for altr,l3regs lookup failed\n"); 158 + return PTR_ERR(priv->l3reg); 159 + } 160 + } 161 + 162 + priv->clk = devm_clk_get(dev, NULL); 163 + if (IS_ERR(priv->clk)) { 164 + dev_err(dev, "no clock specified\n"); 165 + return PTR_ERR(priv->clk); 166 + } 167 + 168 + ret = clk_prepare_enable(priv->clk); 169 + if (ret) { 170 + dev_err(dev, "could not enable clock\n"); 171 + return -EBUSY; 172 + } 173 + 174 + spin_lock_init(&l3_remap_lock); 175 + 176 + if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { 177 + if (enable > 1) { 178 + dev_warn(dev, "invalid bridge-enable %u > 1\n", enable); 179 + } else { 180 + dev_info(dev, "%s bridge\n", 181 + (enable ? "enabling" : "disabling")); 182 + 183 + ret = _alt_hps2fpga_enable_set(priv, enable); 184 + if (ret) { 185 + fpga_bridge_unregister(&pdev->dev); 186 + return ret; 187 + } 188 + } 189 + } 190 + 191 + return fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops, 192 + priv); 193 + } 194 + 195 + static int alt_fpga_bridge_remove(struct platform_device *pdev) 196 + { 197 + struct fpga_bridge *bridge = platform_get_drvdata(pdev); 198 + struct altera_hps2fpga_data *priv = bridge->priv; 199 + 200 + fpga_bridge_unregister(&pdev->dev); 201 + 202 + clk_disable_unprepare(priv->clk); 203 + 204 + return 0; 205 + } 206 + 207 + MODULE_DEVICE_TABLE(of, altera_fpga_of_match); 208 + 209 + static struct platform_driver alt_fpga_bridge_driver = { 210 + .probe = alt_fpga_bridge_probe, 211 + .remove = alt_fpga_bridge_remove, 212 + .driver = { 213 + .name = "altera_hps2fpga_bridge", 214 + .of_match_table = of_match_ptr(altera_fpga_of_match), 215 + }, 216 + }; 217 + 218 + module_platform_driver(alt_fpga_bridge_driver); 219 + 220 + MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge"); 221 + MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); 222 + MODULE_LICENSE("GPL v2");